Merge "Ensure the device wakes up from doze when tapping expand button" into sc-dev am: ba1e18b5c7 am: 5a0546541e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15327940

Change-Id: I8387c771e61152ef836abd56554b34cf61056872
diff --git a/Android.bp b/Android.bp
index 157bdcb..f06c35b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,7 @@
         ":framework_native_aidl",
         ":gatekeeper_aidl",
         ":gsiservice_aidl",
+        ":guiconstants_aidl",
         ":idmap2_aidl",
         ":idmap2_core_aidl",
         ":incidentcompanion_aidl",
@@ -317,6 +318,7 @@
         "icu4j-platform-compat-config",
         "protolog.conf.json.gz",
         "services-platform-compat-config",
+        "TeleService-platform-compat-config",
         "documents-ui-compat-config",
         "calendar-provider-compat-config",
     ],
@@ -328,6 +330,8 @@
     ],
     sdk_version: "core_platform",
     static_libs: [
+        // TODO(b/184162091)
+        "android.hardware.soundtrigger3-V1-java",
         "bouncycastle-repackaged-unbundled",
         "framework-internal-utils",
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 0aed5d9..8cfc024 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -149,6 +149,7 @@
     args: metalava_framework_docs_args +
         " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
     write_sdk_values: true,
+    api_levels_sdk_type: "system",
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/OWNERS b/OWNERS
index 4970dd1..03cfac9 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,23 +1,23 @@
 # This top-level list should remain narrowly defined as team leads; individual
 # teams are strongly encouraged to define narrower OWNERS files at deeper
 # levels within the source tree; see OWNERS.md for more details
-akulian@google.com
-dsandler@android.com
-dsandler@google.com
-hackbod@android.com
-hackbod@google.com
-jjaggi@google.com
-jsharkey@android.com
-jsharkey@google.com
-lorenzo@google.com
-michaelwr@google.com
-nandana@google.com
-narayan@google.com
-ogunwale@google.com
-roosa@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-yamasani@google.com
+akulian@google.com #{LAST_RESORT_SUGGESTION}
+dsandler@android.com #{LAST_RESORT_SUGGESTION}
+dsandler@google.com #{LAST_RESORT_SUGGESTION}
+hackbod@android.com #{LAST_RESORT_SUGGESTION}
+hackbod@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
+jsharkey@android.com #{LAST_RESORT_SUGGESTION}
+jsharkey@google.com #{LAST_RESORT_SUGGESTION}
+lorenzo@google.com #{LAST_RESORT_SUGGESTION}
+michaelwr@google.com #{LAST_RESORT_SUGGESTION}
+nandana@google.com #{LAST_RESORT_SUGGESTION}
+narayan@google.com #{LAST_RESORT_SUGGESTION}
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+roosa@google.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@android.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@google.com #{LAST_RESORT_SUGGESTION}
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
 
 # API changes are already covered by API-Review+1 (http://mdb/android-api-council)
 # via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
@@ -31,3 +31,5 @@
 per-file ApiDocs.bp = file:platform/build/soong:/OWNERS
 per-file StubLibraries.bp = file:platform/build/soong:/OWNERS
 per-file ProtoLibraries.bp = file:platform/build/soong:/OWNERS
+per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
+per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index 7e3cc27..db5ba2f 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -98,7 +98,7 @@
     },
     // Protos have lots of MissingOverride and similar.
     errorprone: {
-        javacflags: ["-XepDisableAllChecks"],
+        enabled: false,
     },
 }
 
@@ -124,6 +124,10 @@
         "libs/incident/proto/android/os/**/*.proto",
         ":service-permission-protos",
     ],
+    // Protos have lots of MissingOverride and similar.
+    errorprone: {
+        enabled: false,
+    },
 }
 
 // ====  java proto device library (for test only)  ==============================
@@ -150,7 +154,7 @@
     sdk_version: "core_current",
     // Protos have lots of MissingOverride and similar.
     errorprone: {
-        javacflags: ["-XepDisableAllChecks"],
+        enabled: false,
     },
 }
 
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 941a1fa..44c55c2 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -410,7 +410,7 @@
     ],
     static_libs: [
         "android-non-updatable.stubs.module_lib",
-        "art.module.public.api.stubs",
+        "art.module.public.api.stubs.module_lib",
     ],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 85dd0c4..766b8c4 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -23,15 +23,15 @@
 
 android_test_helper_app {
     name: "LargeResourcesCompressed",
-    static_libs: [ "androidx.appcompat_appcompat" ],
+    static_libs: ["androidx.appcompat_appcompat"],
 }
 
 genrule {
     name: "LargeResourcesUncompressed",
-    srcs: [ ":LargeResourcesCompressed" ],
+    srcs: [":LargeResourcesCompressed"],
     out: ["LargeResourcesUncompressed.apk"],
-    cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
-         + " && zip $(out) resources.arsc"
+    cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc -d $(genDir)" +
+        " && zip -j $(out) $(genDir)/resources.arsc",
 }
 
 java_library {
diff --git a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
index b24bf42..c299e99 100644
--- a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
@@ -26,6 +26,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 @RunWith(AndroidJUnit4.class)
@@ -39,6 +40,38 @@
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     @Test
+    public void testForEach_Small() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Consumer<Integer> consumer = (i) -> {
+        };
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_SMALL; j++) {
+                    set.add(j);
+                }
+                set.forEach(consumer);
+            }
+        }
+    }
+
+    @Test
+    public void testForEach_Large() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Consumer<Integer> consumer = (i) -> {
+        };
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_LARGE; j++) {
+                    set.add(j);
+                }
+                set.forEach(consumer);
+            }
+        }
+    }
+
+    @Test
     public void testValueAt_InBounds() {
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         ArraySet<Integer> set = new ArraySet<>();
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
index ab3c50b..a1383e6 100644
--- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -24,8 +24,6 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static org.junit.Assert.assertTrue;
-
 import android.annotation.UiThread;
 import android.app.Activity;
 import android.content.ComponentName;
@@ -39,6 +37,7 @@
 import android.perftests.utils.TraceMarkParser;
 import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,6 +52,7 @@
 import androidx.test.filters.LargeTest;
 
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
 
 import junit.framework.Assert;
 
@@ -185,7 +185,6 @@
     public static class BaselineIme extends InputMethodService {
 
         public static final int HEIGHT_DP = 100;
-        private static int sPid;
 
         @Override
         public View onCreateInputView() {
@@ -196,14 +195,10 @@
             view.setPadding(0, 0, 0, 0);
             view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height));
             inner.setBackgroundColor(0xff01fe10); // green
-            sPid = Process.myPid();
+            Log.v(TAG, "onCreateInputView");
             return view;
         }
 
-        static int getPid() {
-            return sPid;
-        }
-
         static ComponentName getName(Context context) {
             return new ComponentName(context, BaselineIme.class);
         }
@@ -281,9 +276,16 @@
     }
 
     private void killBaselineIme() {
-        assertTrue("PID of test and IME can't be same",
-                Process.myPid() != BaselineIme.getPid());
-        Process.killProcess(BaselineIme.getPid());
+        // pidof returns a space separated list of numeric PIDs.
+        String result = SystemUtil.runShellCommand(
+                "pidof com.android.perftests.inputmethod:BaselineIME");
+        for (String pid : result.trim().split(" ")) {
+            // The output may be empty if there is no process with the name.
+            if (TextUtils.isEmpty(pid)) {
+                continue;
+            }
+            Process.killProcess(Integer.parseInt(pid));
+        }
     }
 
     private void testShowOrHideImeWarm(final boolean show) throws Throwable {
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index b1c42a9..42ef80c 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -145,9 +145,11 @@
     @Test
     public void createUser() {
         while (mRunner.keepRunning()) {
+            Log.i(TAG, "Starting timer");
             final int userId = createUserNoFlags();
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -156,6 +158,7 @@
     @Test
     public void createAndStartUser() throws RemoteException {
         while (mRunner.keepRunning()) {
+            Log.i(TAG, "Starting timer");
             final int userId = createUserNoFlags();
 
             final CountDownLatch latch = new CountDownLatch(1);
@@ -166,6 +169,7 @@
             waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -181,12 +185,14 @@
             final int userId = createUserNoFlags();
             final CountDownLatch latch = new CountDownLatch(1);
             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             mIam.startUserInBackground(userId);
             waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -200,12 +206,14 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int userId = createUserNoFlags();
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             // Waits for UserState.mUnlockProgress.finish().
             startUserInBackgroundAndWaitForUnlock(userId);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -217,11 +225,13 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int userId = createUserNoFlags();
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             switchUser(userId);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             switchUserNoCheck(startUser);
             removeUser(userId);
             mRunner.resumeTiming();
@@ -237,6 +247,7 @@
             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
             final CountDownLatch latch = new CountDownLatch(1);
             registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             mAm.switchUser(testUser);
@@ -244,6 +255,7 @@
 
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             switchUserNoCheck(startUser);
             removeUser(testUser);
             mRunner.resumeTiming();
@@ -257,11 +269,13 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             switchUser(testUser);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             switchUserNoCheck(startUser);
             removeUser(testUser);
             mRunner.resumeTiming();
@@ -277,11 +291,13 @@
             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
             mIam.startUserInBackground(userId);
             waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             stopUser(userId, false);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -295,12 +311,14 @@
             final int userId = createUserNoFlags();
             final CountDownLatch latch = new CountDownLatch(1);
             registerUserSwitchObserver(null, latch, userId);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             mAm.switchUser(userId);
             waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             switchUserNoCheck(startUser);
             removeUser(userId);
             mRunner.resumeTiming();
@@ -326,12 +344,14 @@
             }, new IntentFilter(Intent.ACTION_USER_STOPPED));
             final CountDownLatch switchLatch = new CountDownLatch(1);
             registerUserSwitchObserver(switchLatch, null, startUser);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             mAm.switchUser(startUser);
             waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             try {
                 switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
@@ -348,9 +368,11 @@
         assumeTrue(mHasManagedUserFeature);
 
         while (mRunner.keepRunning()) {
+            Log.i(TAG, "Starting timer");
             final int userId = createManagedProfile();
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
             removeUser(userId);
             mRunner.resumeTiming();
@@ -365,11 +387,13 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int userId = createManagedProfile();
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             startUserInBackgroundAndWaitForUnlock(userId);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -386,11 +410,13 @@
             // Start the profile initially, then stop it. Similar to setQuietModeEnabled.
             startUserInBackgroundAndWaitForUnlock(userId);
             stopUser(userId, true);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             startUserInBackgroundAndWaitForUnlock(userId);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -408,12 +434,14 @@
             final int userId = createManagedProfile();
             WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
             installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             startUserInBackgroundAndWaitForUnlock(userId);
             startApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -438,12 +466,14 @@
             startApp(userId, DUMMY_PACKAGE_NAME);
             stopUser(userId, true);
             SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             startUserInBackgroundAndWaitForUnlock(userId);
             startApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -457,11 +487,13 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int userId = createManagedProfile();
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -478,6 +510,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             final int userId = createManagedProfile();
@@ -486,6 +519,7 @@
             startApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -500,11 +534,13 @@
             mRunner.pauseTiming();
             final int userId = createManagedProfile();
             startUserInBackgroundAndWaitForUnlock(userId);
+            Log.i(TAG, "Starting timer");
             mRunner.resumeTiming();
 
             stopUser(userId, true);
 
             mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
             removeUser(userId);
             mRunner.resumeTiming();
         }
@@ -523,11 +559,13 @@
             while (mRunner.keepRunning()) {
                 mRunner.pauseTiming();
                 final int userId = createManagedProfile();
+                Log.i(TAG, "Starting timer");
                 mRunner.resumeTiming();
 
                 startUserInBackgroundAndWaitForUnlock(userId);
 
                 mRunner.pauseTiming();
+                Log.i(TAG, "Stopping timer");
                 removeUser(userId);
                 mRunner.resumeTiming();
             }
@@ -546,11 +584,13 @@
             while (mRunner.keepRunning()) {
                 mRunner.pauseTiming();
                 final int userId = createManagedProfile();
+                Log.i(TAG, "Starting timer");
                 mRunner.resumeTiming();
 
                 startUserInBackgroundAndWaitForUnlock(userId);
 
                 mRunner.pauseTiming();
+                Log.i(TAG, "Stopping timer");
                 removeUser(userId);
                 mRunner.resumeTiming();
             }
diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
index 14a3f8f..93b06e8 100644
--- a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
+++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
@@ -80,21 +80,21 @@
       atrace_apps: "*"
 
       atrace_categories: "am"
-      atrace_categories: "bionic"
-      atrace_categories: "camera"
-      atrace_categories: "wm"
-      atrace_categories: "dalvik"
-      atrace_categories: "sched"
-      atrace_categories: "freq"
-      atrace_categories: "gfx"
-      atrace_categories: "view"
-      atrace_categories: "webview"
-      atrace_categories: "input"
-      atrace_categories: "hal"
       atrace_categories: "binder_driver"
+      atrace_categories: "bionic"
+      atrace_categories: "dalvik"
+      atrace_categories: "input"
+      atrace_categories: "pm"
+      atrace_categories: "res"
+      atrace_categories: "rro"
+      atrace_categories: "ss"
+      atrace_categories: "view"
+      atrace_categories: "wm"
+
+      atrace_categories: "freq"
+      atrace_categories: "sched"
       atrace_categories: "sync"
       atrace_categories: "workq"
-      atrace_categories: "res"
 
     }
   }
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index e192861..73bff08 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -266,9 +266,9 @@
     public void sendFullStatusReport(Instrumentation instrumentation, String key) {
         Log.i(TAG, key + summaryLine());
         Bundle status = new Bundle();
-        status.putLong(key + "_median", median());
-        status.putLong(key + "_mean", mean());
-        status.putLong(key + "_min", min());
+        status.putLong(key + "_median (ns)", median());
+        status.putLong(key + "_mean (ns)", mean());
+        status.putLong(key + "_min (ns)", min());
         status.putLong(key + "_standardDeviation", standardDeviation());
         instrumentation.sendStatus(Activity.RESULT_OK, status);
     }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 144536e..646a027 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -324,6 +324,7 @@
     private final NetworkRequest networkRequest;
     private final long networkDownloadBytes;
     private final long networkUploadBytes;
+    private final long minimumNetworkChunkBytes;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
     private final boolean isPeriodic;
@@ -515,6 +516,17 @@
     }
 
     /**
+     * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+     *
+     * @return Smallest piece of data that cannot be easily paused and resumed, or
+     *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+     * @see Builder#setMinimumNetworkChunkBytes(long)
+     */
+    public @BytesLong long getMinimumNetworkChunkBytes() {
+        return minimumNetworkChunkBytes;
+    }
+
+    /**
      * Set for a job that does not recur periodically, to specify a delay after which the job
      * will be eligible for execution. This value is not set if the job recurs periodically.
      * @see JobInfo.Builder#setMinimumLatency(long)
@@ -679,6 +691,9 @@
         if (networkUploadBytes != j.networkUploadBytes) {
             return false;
         }
+        if (minimumNetworkChunkBytes != j.minimumNetworkChunkBytes) {
+            return false;
+        }
         if (minLatencyMillis != j.minLatencyMillis) {
             return false;
         }
@@ -741,6 +756,7 @@
         }
         hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
         hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
+        hashCode = 31 * hashCode + Long.hashCode(minimumNetworkChunkBytes);
         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -777,6 +793,7 @@
         }
         networkDownloadBytes = in.readLong();
         networkUploadBytes = in.readLong();
+        minimumNetworkChunkBytes = in.readLong();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
         isPeriodic = in.readInt() == 1;
@@ -807,6 +824,7 @@
         networkRequest = b.mNetworkRequest;
         networkDownloadBytes = b.mNetworkDownloadBytes;
         networkUploadBytes = b.mNetworkUploadBytes;
+        minimumNetworkChunkBytes = b.mMinimumNetworkChunkBytes;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
         isPeriodic = b.mIsPeriodic;
@@ -851,6 +869,7 @@
         }
         out.writeLong(networkDownloadBytes);
         out.writeLong(networkUploadBytes);
+        out.writeLong(minimumNetworkChunkBytes);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
         out.writeInt(isPeriodic ? 1 : 0);
@@ -986,6 +1005,7 @@
         private NetworkRequest mNetworkRequest;
         private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
         private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+        private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
         private long mTriggerContentUpdateDelay = -1;
         private long mTriggerContentMaxDelay = -1;
@@ -1038,6 +1058,7 @@
             mNetworkRequest = job.getRequiredNetwork();
             mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
             mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+            mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
             mTriggerContentUris = job.getTriggerContentUris() != null
                     ? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null;
             mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay();
@@ -1256,6 +1277,39 @@
         }
 
         /**
+         * Set the minimum size of non-resumable network traffic this job requires, in bytes. When
+         * the upload or download can be easily paused and resumed, use this to set the smallest
+         * size that must be transmitted between start and stop events to be considered successful.
+         * If the transfer cannot be paused and resumed, then this should be the sum of the values
+         * provided to {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)}.
+         *
+         * <p>
+         * Apps are encouraged to provide values that are as accurate as possible since JobScheduler
+         * will try to run the job at a time when at least the minimum chunk can be transmitted to
+         * reduce the amount of repetitive data that's transferred. Jobs that cannot provide
+         * reasonable estimates should use the sentinel value {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
+         *
+         * <p>
+         * The values provided here only reflect the minimum non-resumable traffic that will be
+         * performed by the base job; if you're using {@link JobWorkItem} then
+         * you also need to define the network traffic used by each work item
+         * when constructing them.
+         *
+         * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and
+         *                       resumed, in bytes.
+         * @see JobInfo#getMinimumNetworkChunkBytes()
+         * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long)
+         */
+        @NonNull
+        public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) {
+            if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) {
+                throw new IllegalArgumentException("Minimum chunk size must be positive");
+            }
+            mMinimumNetworkChunkBytes = chunkSizeBytes;
+            return this;
+        }
+
+        /**
          * Specify that to run this job, the device must be charging (or be a
          * non-battery-powered device connected to permanent power, such as Android TV
          * devices). This defaults to {@code false}.
@@ -1647,12 +1701,29 @@
     /**
      * @hide
      */
-    public void enforceValidity() {
-        // Check that network estimates require network type
-        if ((networkDownloadBytes > 0 || networkUploadBytes > 0) && networkRequest == null) {
+    public final void enforceValidity() {
+        // Check that network estimates require network type and are reasonable values.
+        if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
+                && networkRequest == null) {
             throw new IllegalArgumentException(
                     "Can't provide estimated network usage without requiring a network");
         }
+        final long estimatedTransfer;
+        if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            estimatedTransfer = networkDownloadBytes;
+        } else {
+            estimatedTransfer = networkUploadBytes
+                    + (networkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : networkDownloadBytes);
+        }
+        if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN
+                && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+                && minimumNetworkChunkBytes > estimatedTransfer) {
+            throw new IllegalArgumentException(
+                    "Minimum chunk size can't be greater than estimated network usage");
+        }
+        if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN && minimumNetworkChunkBytes <= 0) {
+            throw new IllegalArgumentException("Minimum chunk size must be positive");
+        }
 
         // Check that a deadline was not set on a periodic job.
         if (isPeriodic) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 32655c7..acc661e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -44,6 +44,9 @@
 public class JobParameters implements Parcelable {
 
     /** @hide */
+    public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
+
+    /** @hide */
     public static final int INTERNAL_STOP_REASON_CANCELED =
             JobProtoEnums.INTERNAL_STOP_REASON_CANCELLED; // 0.
     /** @hide */
@@ -106,6 +109,7 @@
      * @hide
      */
     public static final int[] JOB_STOP_REASON_CODES = {
+            INTERNAL_STOP_REASON_UNKNOWN,
             INTERNAL_STOP_REASON_CANCELED,
             INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
             INTERNAL_STOP_REASON_PREEMPT,
@@ -269,7 +273,7 @@
     private final Network network;
 
     private int mStopReason = STOP_REASON_UNDEFINED;
-    private int mInternalStopReason; // Default value is REASON_CANCELED
+    private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
     private String debugStopReason; // Human readable stop reason for debugging.
 
     /** @hide */
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
index 0c45cbf..372f9fa 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
@@ -19,6 +19,7 @@
 import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
 
 import android.annotation.BytesLong;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Build;
@@ -33,8 +34,9 @@
 final public class JobWorkItem implements Parcelable {
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Intent mIntent;
-    final long mNetworkDownloadBytes;
-    final long mNetworkUploadBytes;
+    private final long mNetworkDownloadBytes;
+    private final long mNetworkUploadBytes;
+    private final long mMinimumChunkBytes;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     int mDeliveryCount;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -49,9 +51,7 @@
      * @param intent The general Intent describing this work.
      */
     public JobWorkItem(Intent intent) {
-        mIntent = intent;
-        mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
-        mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+        this(intent, NETWORK_BYTES_UNKNOWN, NETWORK_BYTES_UNKNOWN);
     }
 
     /**
@@ -68,9 +68,45 @@
      *            uploaded by this job work item, in bytes.
      */
     public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+        this(intent, downloadBytes, uploadBytes, NETWORK_BYTES_UNKNOWN);
+    }
+
+    /**
+     * Create a new piece of work, which can be submitted to
+     * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+     * <p>
+     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+     * details about how to estimate network traffic.
+     *
+     * @param intent            The general Intent describing this work.
+     * @param downloadBytes     The estimated size of network traffic that will be
+     *                          downloaded by this job work item, in bytes.
+     * @param uploadBytes       The estimated size of network traffic that will be
+     *                          uploaded by this job work item, in bytes.
+     * @param minimumChunkBytes The smallest piece of data that cannot be easily paused and
+     *                          resumed, in bytes.
+     */
+    public JobWorkItem(@Nullable Intent intent, @BytesLong long downloadBytes,
+            @BytesLong long uploadBytes, @BytesLong long minimumChunkBytes) {
+        if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && minimumChunkBytes <= 0) {
+            throw new IllegalArgumentException("Minimum chunk size must be positive");
+        }
+        final long estimatedTransfer;
+        if (uploadBytes == NETWORK_BYTES_UNKNOWN) {
+            estimatedTransfer = downloadBytes;
+        } else {
+            estimatedTransfer = uploadBytes
+                    + (downloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : downloadBytes);
+        }
+        if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+                && minimumChunkBytes > estimatedTransfer) {
+            throw new IllegalArgumentException(
+                    "Minimum chunk size can't be greater than estimated network usage");
+        }
         mIntent = intent;
         mNetworkDownloadBytes = downloadBytes;
         mNetworkUploadBytes = uploadBytes;
+        mMinimumChunkBytes = minimumChunkBytes;
     }
 
     /**
@@ -103,6 +139,16 @@
     }
 
     /**
+     * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+     *
+     * @return Smallest piece of data that cannot be easily paused and resumed, or
+     * {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+     */
+    public @BytesLong long getMinimumNetworkChunkBytes() {
+        return mMinimumChunkBytes;
+    }
+
+    /**
      * Return the count of the number of times this work item has been delivered
      * to the job.  The value will be > 1 if it has been redelivered because the job
      * was stopped or crashed while it had previously been delivered but before the
@@ -161,6 +207,10 @@
             sb.append(" uploadBytes=");
             sb.append(mNetworkUploadBytes);
         }
+        if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" minimumChunkBytes=");
+            sb.append(mMinimumChunkBytes);
+        }
         if (mDeliveryCount != 0) {
             sb.append(" dcount=");
             sb.append(mDeliveryCount);
@@ -169,6 +219,28 @@
         return sb.toString();
     }
 
+    /**
+     * @hide
+     */
+    public void enforceValidity() {
+        final long estimatedTransfer;
+        if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            estimatedTransfer = mNetworkDownloadBytes;
+        } else {
+            estimatedTransfer = mNetworkUploadBytes
+                    + (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : mNetworkDownloadBytes);
+        }
+        if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN
+                && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+                && mMinimumChunkBytes > estimatedTransfer) {
+            throw new IllegalArgumentException(
+                    "Minimum chunk size can't be greater than estimated network usage");
+        }
+        if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN && mMinimumChunkBytes <= 0) {
+            throw new IllegalArgumentException("Minimum chunk size must be positive");
+        }
+    }
+
     public int describeContents() {
         return 0;
     }
@@ -182,6 +254,7 @@
         }
         out.writeLong(mNetworkDownloadBytes);
         out.writeLong(mNetworkUploadBytes);
+        out.writeLong(mMinimumChunkBytes);
         out.writeInt(mDeliveryCount);
         out.writeInt(mWorkId);
     }
@@ -206,6 +279,7 @@
         }
         mNetworkDownloadBytes = in.readLong();
         mNetworkUploadBytes = in.readLong();
+        mMinimumChunkBytes = in.readLong();
         mDeliveryCount = in.readInt();
         mWorkId = in.readInt();
     }
diff --git a/apex/jobscheduler/framework/java/android/app/tare/OWNERS b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 8c06338..968c6e5 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -71,6 +71,13 @@
 
     long getTimeSinceLastJobRun(String packageName, int userId);
 
+    /**
+     * Returns the time (in milliseconds) since the app was last interacted with by the user.
+     * This can be larger than the current elapsedRealtime, in case it happened before boot or
+     * a really large value if the app was never interacted with.
+     */
+    long getTimeSinceLastUsedByUser(String packageName, int userId);
+
     void onUserRemoved(int userId);
 
     void addListener(AppIdleStateChangeListener listener);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 45588e8..03465fd 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2150,7 +2150,7 @@
         private LocationManager mLocationManager;
 
         Injector(Context ctx) {
-            mContext = ctx;
+            mContext = ctx.createAttributionContext(TAG);
         }
 
         AlarmManager getAlarmManager() {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 5a13a84..143c0f1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -804,28 +804,42 @@
             @WorkType final int workType) {
         final List<StateController> controllers = mService.mControllers;
         final int numControllers = controllers.size();
-        for (int ic = 0; ic < numControllers; ic++) {
-            controllers.get(ic).prepareForExecutionLocked(jobStatus);
-        }
-        final PackageStats packageStats =
-                getPkgStatsLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
-        packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
-        if (!worker.executeRunnableJob(jobStatus, workType)) {
-            Slog.e(TAG, "Error executing " + jobStatus);
-            mWorkCountTracker.onStagedJobFailed(workType);
+        final PowerManager.WakeLock wl =
+                mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getTag());
+        wl.setWorkSource(mService.deriveWorkSource(
+                jobStatus.getSourceUid(), jobStatus.getSourcePackageName()));
+        wl.setReferenceCounted(false);
+        // Since the quota controller will start counting from the time prepareForExecutionLocked()
+        // is called, hold a wakelock to make sure the CPU doesn't suspend between that call and
+        // when the service actually starts.
+        wl.acquire();
+        try {
             for (int ic = 0; ic < numControllers; ic++) {
-                controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+                controllers.get(ic).prepareForExecutionLocked(jobStatus);
             }
-        } else {
-            mRunningJobs.add(jobStatus);
-            mWorkCountTracker.onJobStarted(workType);
-            packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
-            mActivePkgStats.add(
-                    jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats);
-        }
-        final List<JobStatus> pendingJobs = mService.mPendingJobs;
-        if (pendingJobs.remove(jobStatus)) {
-            mService.mJobPackageTracker.noteNonpending(jobStatus);
+            final PackageStats packageStats = getPkgStatsLocked(
+                    jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+            packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
+            if (!worker.executeRunnableJob(jobStatus, workType)) {
+                Slog.e(TAG, "Error executing " + jobStatus);
+                mWorkCountTracker.onStagedJobFailed(workType);
+                for (int ic = 0; ic < numControllers; ic++) {
+                    controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+                }
+            } else {
+                mRunningJobs.add(jobStatus);
+                mWorkCountTracker.onJobStarted(workType);
+                packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
+                mActivePkgStats.add(
+                        jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+                        packageStats);
+            }
+            final List<JobStatus> pendingJobs = mService.mPendingJobs;
+            if (pendingJobs.remove(jobStatus)) {
+                mService.mJobPackageTracker.noteNonpending(jobStatus);
+            }
+        } finally {
+            wl.release();
         }
     }
 
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 78670c7..4fb0b71 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -143,6 +143,7 @@
  *
  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
+ *
  * @hide
  */
 public class JobSchedulerService extends com.android.server.SystemService
@@ -201,7 +202,7 @@
     };
 
     @VisibleForTesting
-    public static Clock sElapsedRealtimeClock =  new MySimpleClock(ZoneOffset.UTC) {
+    public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
         @Override
         public long millis() {
             return SystemClock.elapsedRealtime();
@@ -226,6 +227,7 @@
     static final int MSG_UID_GONE = 5;
     static final int MSG_UID_ACTIVE = 6;
     static final int MSG_UID_IDLE = 7;
+    static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
 
     /**
      * Track Services that have currently active or pending jobs. The index is provided by
@@ -315,7 +317,7 @@
     /**
      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
      */
-    final SparseIntArray mBackingUpUids = new SparseIntArray();
+    private final SparseBooleanArray mBackingUpUids = new SparseBooleanArray();
 
     /**
      * Cache of debuggable app status.
@@ -325,6 +327,10 @@
     /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
     private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
 
+    /** List of jobs whose controller state has changed since the last time we evaluated the job. */
+    @GuardedBy("mLock")
+    private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
+
     /**
      * Named indices into standby bucket arrays, for clarity in referring to
      * specific buckets' bookkeeping.
@@ -601,7 +607,7 @@
                             KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
             API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
+                    KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
             API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
@@ -812,9 +818,10 @@
                                 try {
                                     final int userId = UserHandle.getUserId(pkgUid);
                                     IPackageManager pm = AppGlobals.getPackageManager();
-                                    final int state = pm.getApplicationEnabledSetting(pkgName, userId);
+                                    final int state =
+                                            pm.getApplicationEnabledSetting(pkgName, userId);
                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
-                                            || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
+                                            || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
                                         if (DEBUG) {
                                             Slog.d(TAG, "Removing jobs for package " + pkgName
                                                     + " in user " + userId);
@@ -830,7 +837,7 @@
                                                     "app disabled");
                                         }
                                     }
-                                } catch (RemoteException|IllegalArgumentException e) {
+                                } catch (RemoteException | IllegalArgumentException e) {
                                     /*
                                      * IllegalArgumentException means that the package doesn't exist.
                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
@@ -866,16 +873,15 @@
                     }
                 }
             } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
-                int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
                 if (DEBUG) {
-                    Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+                    Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
                 }
                 synchronized (mLock) {
-                    mUidToPackageCache.remove(uidRemoved);
+                    mUidToPackageCache.remove(pkgUid);
                     // There's no guarantee that the process has been stopped by the time we
                     // get here, but since this is generally a user-initiated action, it should
                     // be fine to just put USER instead of UNINSTALL or DISABLED.
-                    cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+                    cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
                             JobParameters.STOP_REASON_USER,
                             JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
                     for (int c = 0; c < mControllers.size(); ++c) {
@@ -984,8 +990,18 @@
         return mConstants;
     }
 
-    public boolean isChainedAttributionEnabled() {
-        return WorkSource.isChainedBatteryAttributionEnabled(getContext());
+    @NonNull
+    public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
+        if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+            WorkSource ws = new WorkSource();
+            ws.createWorkChain()
+                    .addNode(sourceUid, sourcePackageName)
+                    .addNode(Process.SYSTEM_UID, "JobScheduler");
+            return ws;
+        } else {
+            return sourcePackageName == null
+                    ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName);
+        }
     }
 
     @Nullable
@@ -1133,7 +1149,7 @@
                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
                     Slog.w(TAG, "Too many jobs for uid " + uId);
                     throw new IllegalStateException("Apps may not schedule more than "
-                                + MAX_JOBS_PER_APP + " distinct jobs");
+                            + MAX_JOBS_PER_APP + " distinct jobs");
                 }
             }
 
@@ -1669,6 +1685,7 @@
 
     /**
      * Called when we want to remove a JobStatus object that we've finished executing.
+     *
      * @return true if the job was removed.
      */
     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
@@ -1679,7 +1696,7 @@
         // Remove from store as well as controllers.
         final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
         if (removed && mReadyToRock) {
-            for (int i=0; i<mControllers.size(); i++) {
+            for (int i = 0; i < mControllers.size(); i++) {
                 StateController controller = mControllers.get(i);
                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
             }
@@ -1700,17 +1717,29 @@
         return false;
     }
 
+    /** Return {@code true} if the specified job is currently executing. */
+    @GuardedBy("mLock")
+    public boolean isCurrentlyRunningLocked(JobStatus job) {
+        return mConcurrencyManager.isJobRunningLocked(job);
+    }
+
+    private void noteJobPending(JobStatus job) {
+        mJobPackageTracker.notePending(job);
+    }
+
     void noteJobsPending(List<JobStatus> jobs) {
         for (int i = jobs.size() - 1; i >= 0; i--) {
-            JobStatus job = jobs.get(i);
-            mJobPackageTracker.notePending(job);
+            noteJobPending(jobs.get(i));
         }
     }
 
+    private void noteJobNonPending(JobStatus job) {
+        mJobPackageTracker.noteNonpending(job);
+    }
+
     void noteJobsNonpending(List<JobStatus> jobs) {
         for (int i = jobs.size() - 1; i >= 0; i--) {
-            JobStatus job = jobs.get(i);
-            mJobPackageTracker.noteNonpending(job);
+            noteJobNonPending(jobs.get(i));
         }
     }
 
@@ -1723,7 +1752,6 @@
      * @param failureToReschedule Provided job status that we will reschedule.
      * @return A newly instantiated JobStatus with the same constraints as the last job except
      * with adjusted timing constraints.
-     *
      * @see #maybeQueueReadyJobsForExecutionLocked
      */
     @VisibleForTesting
@@ -1765,7 +1793,7 @@
             newJob.setOriginalLatestRunTimeElapsed(
                     failureToReschedule.getOriginalLatestRunTimeElapsed());
         }
-        for (int ic=0; ic<mControllers.size(); ic++) {
+        for (int ic = 0; ic < mControllers.size(); ic++) {
             StateController controller = mControllers.get(ic);
             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
         }
@@ -1951,13 +1979,19 @@
     // StateChangedListener implementations.
 
     /**
-     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
-     * some controller's state has changed, so as to run through the list of jobs and start/stop
-     * any that are eligible.
+     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} to run
+     * through a list of jobs and start/stop any whose status has changed.
      */
     @Override
-    public void onControllerStateChanged() {
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+    public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
+        if (changedJobs == null) {
+            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        } else if (changedJobs.size() > 0) {
+            synchronized (mLock) {
+                mChangedJobList.addAll(changedJobs);
+            }
+            mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
+        }
     }
 
     @Override
@@ -1989,6 +2023,7 @@
                                 mJobPackageTracker.notePending(js);
                                 addOrderedItem(mPendingJobs, js, mPendingJobComparator);
                             }
+                            mChangedJobList.remove(js);
                         } else {
                             Slog.e(TAG, "Given null job to check individually");
                         }
@@ -1997,7 +2032,6 @@
                         if (DEBUG) {
                             Slog.d(TAG, "MSG_CHECK_JOB");
                         }
-                        removeMessages(MSG_CHECK_JOB);
                         if (mReportedActive) {
                             // if jobs are currently being run, queue all ready jobs for execution.
                             queueReadyJobsForExecutionLocked();
@@ -2012,6 +2046,12 @@
                         }
                         queueReadyJobsForExecutionLocked();
                         break;
+                    case MSG_CHECK_CHANGED_JOB_LIST:
+                        if (DEBUG) {
+                            Slog.d(TAG, "MSG_CHECK_CHANGED_JOB_LIST");
+                        }
+                        checkChangedJobListLocked();
+                        break;
                     case MSG_STOP_JOB:
                         cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
                                 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
@@ -2099,11 +2139,8 @@
                 continue;
             }
             if (!running.isReady()) {
-                // If a restricted job doesn't have dynamic constraints satisfied, assume that's
-                // the reason the job is being stopped, instead of because of other constraints
-                // not being satisfied.
                 if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
-                        && !running.areDynamicConstraintsSatisfied()) {
+                        && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
                     serviceContext.cancelExecutingJobLocked(
                             running.getStopReason(),
                             JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
@@ -2141,6 +2178,11 @@
         // MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready
         // jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue.
         mHandler.removeMessages(MSG_CHECK_JOB);
+        // MSG_CHECK_CHANGED_JOB_LIST is a weaker form of _GREEDY. Since we're checking and queueing
+        // all ready jobs, we don't need to keep any MSG_CHECK_CHANGED_JOB_LIST messages in the
+        // queue.
+        mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+        mChangedJobList.clear();
         if (DEBUG) {
             Slog.d(TAG, "queuing all ready jobs for execution:");
         }
@@ -2203,17 +2245,21 @@
             reset();
         }
 
-        // Functor method invoked for each job via JobStore.forEachJob()
         @Override
         public void accept(JobStatus job) {
-            if (isReadyToBeExecutedLocked(job)) {
+            final boolean isRunning = isCurrentlyRunningLocked(job);
+            if (isReadyToBeExecutedLocked(job, false)) {
                 if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(),
                         job.getJob().getService().getPackageName())) {
                     Slog.w(TAG, "Aborting job " + job.getUid() + ":"
                             + job.getJob().toString() + " -- package not allowed to start");
-                    mHandler.obtainMessage(MSG_STOP_JOB,
-                            JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
-                            .sendToTarget();
+                    if (isRunning) {
+                        mHandler.obtainMessage(MSG_STOP_JOB,
+                                JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
+                                .sendToTarget();
+                    } else if (mPendingJobs.remove(job)) {
+                        noteJobNonPending(job);
+                    }
                     return;
                 }
 
@@ -2246,8 +2292,41 @@
                 } else {
                     unbatchedCount++;
                 }
-                runnableJobs.add(job);
+                if (!isRunning) {
+                    runnableJobs.add(job);
+                }
             } else {
+                if (isRunning) {
+                    final int internalStopReason;
+                    final String debugReason;
+                    if (!job.isReady()) {
+                        if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+                                && job.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
+                            internalStopReason =
+                                    JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET;
+                            debugReason = "cancelled due to restricted bucket";
+                        } else {
+                            internalStopReason =
+                                    JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED;
+                            debugReason = "cancelled due to unsatisfied constraints";
+                        }
+                    } else {
+                        final JobRestriction restriction = checkIfRestricted(job);
+                        if (restriction != null) {
+                            internalStopReason = restriction.getInternalReason();
+                            debugReason = "restricted due to "
+                                    + JobParameters.getInternalReasonCodeDescription(
+                                    internalStopReason);
+                        } else {
+                            internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN;
+                            debugReason = "couldn't figure out why the job should stop running";
+                        }
+                    }
+                    stopJobOnServiceContextLocked(job, job.getStopReason(),
+                            internalStopReason, debugReason);
+                } else if (mPendingJobs.remove(job)) {
+                    noteJobNonPending(job);
+                }
                 evaluateControllerStatesLocked(job);
             }
         }
@@ -2283,10 +2362,16 @@
             runnableJobs.clear();
         }
     }
+
     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
 
     @GuardedBy("mLock")
     private void maybeQueueReadyJobsForExecutionLocked() {
+        mHandler.removeMessages(MSG_CHECK_JOB);
+        // This method will evaluate all jobs, so we don't need to keep any messages for a suubset
+        // of jobs in the queue.
+        mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+        mChangedJobList.clear();
         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
 
         noteJobsNonpending(mPendingJobs);
@@ -2296,6 +2381,18 @@
         mMaybeQueueFunctor.postProcessLocked();
     }
 
+    @GuardedBy("mLock")
+    private void checkChangedJobListLocked() {
+        mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+        if (DEBUG) {
+            Slog.d(TAG, "Check changed jobs...");
+        }
+
+        mChangedJobList.forEach(mMaybeQueueFunctor);
+        mMaybeQueueFunctor.postProcessLocked();
+        mChangedJobList.clear();
+    }
+
     /** Returns true if both the calling and source users for the job are started. */
     @GuardedBy("mLock")
     public boolean areUsersStartedLocked(final JobStatus job) {
@@ -2342,7 +2439,7 @@
 
         final boolean jobExists = mJobs.containsJob(job);
         final boolean userStarted = areUsersStartedLocked(job);
-        final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+        final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
 
         if (DEBUG) {
             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
@@ -2417,7 +2514,7 @@
 
         final boolean jobExists = mJobs.containsJob(job);
         final boolean userStarted = areUsersStartedLocked(job);
-        final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+        final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
 
         if (DEBUG) {
             Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
@@ -2537,7 +2634,7 @@
                 // No need to actually do anything here, since for a full backup the
                 // activity manager will kill the process which will kill the job (and
                 // cause it to restart, but now it can't run).
-                mBackingUpUids.put(uid, uid);
+                mBackingUpUids.put(uid, true);
             }
         }
 
@@ -2675,7 +2772,8 @@
      * Binder stub trampoline implementation
      */
     final class JobSchedulerStub extends IJobScheduler.Stub {
-        /** Cache determination of whether a given app can persist jobs
+        /**
+         * Cache determination of whether a given app can persist jobs
          * key is uid of the calling app; value is undetermined/true/false
          */
         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
@@ -2793,6 +2891,7 @@
                 throw new NullPointerException("work is null");
             }
 
+            work.enforceValidity();
             validateJobFlags(job, uid);
 
             final long ident = Binder.clearCallingIdentity();
@@ -2956,8 +3055,7 @@
         public List<JobInfo> getStartedJobs() {
             final int uid = Binder.getCallingUid();
             if (uid != Process.SYSTEM_UID) {
-                throw new SecurityException(
-                    "getStartedJobs() is system internal use only.");
+                throw new SecurityException("getStartedJobs() is system internal use only.");
             }
 
             final ArrayList<JobInfo> runningJobs;
@@ -2985,8 +3083,7 @@
         public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
             final int uid = Binder.getCallingUid();
             if (uid != Process.SYSTEM_UID) {
-                throw new SecurityException(
-                    "getAllJobSnapshots() is system internal use only.");
+                throw new SecurityException("getAllJobSnapshots() is system internal use only.");
             }
             synchronized (mLock) {
                 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
@@ -3048,7 +3145,7 @@
 
         synchronized (mLock) {
             boolean foundSome = false;
-            for (int i=0; i<mActiveServices.size(); i++) {
+            for (int i = 0; i < mActiveServices.size(); i++) {
                 final JobServiceContext jc = mActiveServices.get(i);
                 final JobStatus js = jc.getRunningJobLocked();
                 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
@@ -3187,7 +3284,7 @@
                     printed = true;
                     pw.println("source-user-stopped");
                 }
-                if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
+                if (mBackingUpUids.get(js.getSourceUid())) {
                     if (printed) {
                         pw.print(" ");
                     }
@@ -3351,7 +3448,7 @@
                     pw.print(" !active=");
                     pw.print(!mConcurrencyManager.isJobRunningLocked(job));
                     pw.print(" !backingup=");
-                    pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
+                    pw.print(!(mBackingUpUids.get(job.getSourceUid())));
                     pw.print(" comp=");
                     pw.print(isComponentUsable(job));
                     pw.println(")");
@@ -3364,7 +3461,7 @@
             }
             pw.decreaseIndent();
 
-            for (int i=0; i<mControllers.size(); i++) {
+            for (int i = 0; i < mControllers.size(); i++) {
                 pw.println();
                 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
                 pw.increaseIndent();
@@ -3373,7 +3470,7 @@
             }
 
             boolean overridePrinted = false;
-            for (int i=0; i< mUidPriorityOverride.size(); i++) {
+            for (int i = 0; i < mUidPriorityOverride.size(); i++) {
                 int uid = mUidPriorityOverride.keyAt(i);
                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
                     if (!overridePrinted) {
@@ -3440,7 +3537,7 @@
             boolean pendingPrinted = false;
             pw.println("Pending queue:");
             pw.increaseIndent();
-            for (int i=0; i<mPendingJobs.size(); i++) {
+            for (int i = 0; i < mPendingJobs.size(); i++) {
                 JobStatus job = mPendingJobs.get(i);
                 if (!predicate.test(job)) {
                     continue;
@@ -3590,7 +3687,8 @@
                         continue;
                     }
 
-                    job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
+                    job.dump(proto,
+                            JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
 
                     proto.write(
                             JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
@@ -3607,7 +3705,7 @@
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
                             mConcurrencyManager.isJobRunningLocked(job));
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
-                            mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
+                            mBackingUpUids.get(job.getSourceUid()));
                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
                             isComponentUsable(job));
 
@@ -3628,7 +3726,7 @@
                 controller.dumpControllerStateLocked(
                         proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
             }
-            for (int i=0; i< mUidPriorityOverride.size(); i++) {
+            for (int i = 0; i < mUidPriorityOverride.size(); i++) {
                 int uid = mUidPriorityOverride.keyAt(i);
                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
                     long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
@@ -3667,8 +3765,8 @@
                 if (job == null) {
                     final long ijToken = proto.start(ActiveJob.INACTIVE);
 
-                        proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
-                                nowElapsed - jsc.mStoppedTime);
+                    proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
+                            nowElapsed - jsc.mStoppedTime);
                     if (jsc.mStoppedReason != null) {
                         proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
                                 jsc.mStoppedReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 3fa1c12..2ebd1ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -42,7 +42,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.WorkSource;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -109,6 +108,7 @@
     private final Object mLock;
     private final IBatteryStats mBatteryStats;
     private final JobPackageTracker mJobPackageTracker;
+    private final PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
 
     // Execution state.
@@ -205,6 +205,7 @@
         mCallbackHandler = new JobServiceHandler(looper);
         mJobConcurrencyManager = concurrencyManager;
         mCompletedListener = service;
+        mPowerManager = mContext.getSystemService(PowerManager.class);
         mAvailable = true;
         mVerb = VERB_FINISHED;
         mPreferredUid = NO_PREFERRED_UID;
@@ -271,6 +272,12 @@
             // it was inflated from disk with not-yet-coherent delay/deadline bounds.
             job.clearPersistedUtcTimes();
 
+            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, job.getTag());
+            mWakeLock.setWorkSource(
+                    mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+            mWakeLock.setReferenceCounted(false);
+            mWakeLock.acquire();
+
             mVerb = VERB_BINDING;
             scheduleOpTimeOutLocked();
             final Intent intent = new Intent().setComponent(job.getServiceComponent());
@@ -306,6 +313,7 @@
                 mRunningCallback = null;
                 mParams = null;
                 mExecutionStartTimeElapsed = 0L;
+                mWakeLock.release();
                 mVerb = VERB_FINISHED;
                 removeOpTimeOutLocked();
                 return false;
@@ -495,42 +503,10 @@
                 return;
             }
             this.service = IJobService.Stub.asInterface(service);
-            final PowerManager pm =
-                    (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    runningJob.getTag());
-            wl.setWorkSource(deriveWorkSource(runningJob));
-            wl.setReferenceCounted(false);
-            wl.acquire();
-
-            // We use a new wakelock instance per job.  In rare cases there is a race between
-            // teardown following job completion/cancellation and new job service spin-up
-            // such that if we simply assign mWakeLock to be the new instance, we orphan
-            // the currently-live lock instead of cleanly replacing it.  Watch for this and
-            // explicitly fast-forward the release if we're in that situation.
-            if (mWakeLock != null) {
-                Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
-                        + " tag=" + mWakeLock.getTag());
-                mWakeLock.release();
-            }
-            mWakeLock = wl;
             doServiceBoundLocked();
         }
     }
 
-    private WorkSource deriveWorkSource(JobStatus runningJob) {
-        final int jobUid = runningJob.getSourceUid();
-        if (WorkSource.isChainedBatteryAttributionEnabled(mContext)) {
-            WorkSource workSource = new WorkSource();
-            workSource.createWorkChain()
-                    .addNode(jobUid, null)
-                    .addNode(android.os.Process.SYSTEM_UID, "JobScheduler");
-            return workSource;
-        } else {
-            return new WorkSource(jobUid);
-        }
-    }
-
     /** If the client service crashes we reschedule this job and clean up. */
     @Override
     public void onServiceDisconnected(ComponentName name) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
index cb3c437..1068bda 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
@@ -17,6 +17,8 @@
 package com.android.server.job;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
 
 import com.android.server.job.controllers.JobStatus;
 
@@ -29,10 +31,10 @@
  */
 public interface StateChangedListener {
     /**
-     * Called by the controller to notify the JobManager that it should check on the state of a
-     * task.
+     * Called by the controller to notify the JobScheduler that it should check on the state of a
+     * set of jobs. If {@code changedJobs} is null, then all registered jobs will be evaluated.
      */
-    public void onControllerStateChanged();
+    void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs);
 
     /**
      * Called by the controller to notify the JobManager that regardless of the state of the task,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 31a0853..0ceab35 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -21,6 +21,7 @@
 
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
@@ -191,8 +192,8 @@
             ));
         }
 
-        if (mUpdateJobFunctor.mChanged) {
-            mStateChangedListener.onControllerStateChanged();
+        if (mUpdateJobFunctor.mChangedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(mUpdateJobFunctor.mChangedJobs);
         }
     }
 
@@ -222,7 +223,7 @@
 
     private final class UpdateJobFunctor implements Consumer<JobStatus> {
         int mActiveState;
-        boolean mChanged = false;
+        final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
         int mTotalCount = 0;
         int mCheckedCount = 0;
         long mUpdateTimeElapsed = 0;
@@ -230,7 +231,7 @@
         void prepare(int newActiveState) {
             mActiveState = newActiveState;
             mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
-            mChanged = false;
+            mChangedJobs.clear();
             mTotalCount = 0;
             mCheckedCount = 0;
         }
@@ -240,7 +241,7 @@
             mTotalCount++;
             mCheckedCount++;
             if (updateSingleJobRestrictionLocked(jobStatus, mUpdateTimeElapsed, mActiveState)) {
-                mChanged = true;
+                mChangedJobs.add(jobStatus);
             }
         }
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 6fd0948..657f470 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -103,14 +103,8 @@
         boolean reportChange = false;
         for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
             final JobStatus ts = mTrackedTasks.valueAt(i);
-            boolean previous = ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
-            if (previous != stablePower) {
-                reportChange = true;
-            }
-            previous = ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
-            if (previous != batteryNotLow) {
-                reportChange = true;
-            }
+            reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
+            reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
         }
         if (stablePower || batteryNotLow) {
             // If one of our conditions has been satisfied, always schedule any newly ready jobs.
@@ -118,7 +112,7 @@
         } else if (reportChange) {
             // Otherwise, just let the job scheduler know the state has changed and take care of it
             // as it thinks is best.
-            mStateChangedListener.onControllerStateChanged();
+            mStateChangedListener.onControllerStateChanged(mTrackedTasks);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 12d9c7f..98a39a6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -28,6 +28,7 @@
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
@@ -220,24 +221,27 @@
     private void updateComponentStatesLocked(@NonNull Predicate<JobStatus> filter) {
         mComponentStateUpdateFunctor.reset();
         mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
-        if (mComponentStateUpdateFunctor.mChanged) {
-            mStateChangedListener.onControllerStateChanged();
+        if (mComponentStateUpdateFunctor.mChangedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(
+                    mComponentStateUpdateFunctor.mChangedJobs);
         }
     }
 
     final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
         @GuardedBy("mLock")
-        boolean mChanged;
+        final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
 
         @Override
         @GuardedBy("mLock")
         public void accept(JobStatus jobStatus) {
-            mChanged |= updateComponentEnabledStateLocked(jobStatus);
+            if (updateComponentEnabledStateLocked(jobStatus)) {
+                mChangedJobs.add(jobStatus);
+            }
         }
 
         @GuardedBy("mLock")
         private void reset() {
-            mChanged = false;
+            mChangedJobs.clear();
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8065aa..321c0b3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -549,6 +549,47 @@
      */
     private boolean isInsane(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
+        // Use the maximum possible time since it gives us an upper bound, even though the job
+        // could end up stopping earlier.
+        final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
+
+        final long minimumChunkBytes = jobStatus.getMinimumNetworkChunkBytes();
+        if (minimumChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+            final long bandwidthDown = capabilities.getLinkDownstreamBandwidthKbps();
+            // If we don't know the bandwidth, all we can do is hope the job finishes the minimum
+            // chunk in time.
+            if (bandwidthDown > 0) {
+                // Divide by 8 to convert bits to bytes.
+                final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+                        / (DataUnit.KIBIBYTES.toBytes(bandwidthDown) / 8));
+                if (estimatedMillis > maxJobExecutionTimeMs) {
+                    // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+                    Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over "
+                            + bandwidthDown + " kbps network would take "
+                            + estimatedMillis + "ms and job has "
+                            + maxJobExecutionTimeMs + "ms to run; that's insane!");
+                    return true;
+                }
+            }
+            final long bandwidthUp = capabilities.getLinkUpstreamBandwidthKbps();
+            // If we don't know the bandwidth, all we can do is hope the job finishes in time.
+            if (bandwidthUp > 0) {
+                // Divide by 8 to convert bits to bytes.
+                final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+                        / (DataUnit.KIBIBYTES.toBytes(bandwidthUp) / 8));
+                if (estimatedMillis > maxJobExecutionTimeMs) {
+                    // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+                    Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over " + bandwidthUp
+                            + " kbps network would take " + estimatedMillis + "ms and job has "
+                            + maxJobExecutionTimeMs + "ms to run; that's insane!");
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        // Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
+
         if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && mChargingTracker.isCharging()) {
             // We're charging and on an unmetered network. We don't have to be as conservative about
@@ -557,9 +598,6 @@
             return false;
         }
 
-        // Use the maximum possible time since it gives us an upper bound, even though the job
-        // could end up stopping earlier.
-        final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
 
         final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
@@ -633,23 +671,34 @@
         }
     }
 
-    private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
+    private boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // Only consider doing this for unrestricted prefetching jobs
         if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
             return false;
         }
+        final long estDownloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
+        if (estDownloadBytes <= 0) {
+            // Need to at least know the estimated download bytes for a prefetch job.
+            return false;
+        }
 
         // See if we match after relaxing any unmetered request
         final NetworkCapabilities.Builder builder =
                 copyCapabilities(jobStatus.getJob().getRequiredNetwork());
         builder.removeCapability(NET_CAPABILITY_NOT_METERED);
-        if (builder.build().satisfiedByNetworkCapabilities(capabilities)) {
-            // TODO: treat this as "maybe" response; need to check quotas
-            return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
-        } else {
-            return false;
+        if (builder.build().satisfiedByNetworkCapabilities(capabilities)
+                && jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC) {
+            final long opportunisticQuotaBytes =
+                    mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+                            network, NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS);
+            final long estUploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
+            final long estimatedBytes = estDownloadBytes
+                    + (estUploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN ? 0 : estUploadBytes);
+            return opportunisticQuotaBytes >= estimatedBytes;
         }
+
+        return false;
     }
 
     @VisibleForTesting
@@ -796,12 +845,14 @@
 
         mSortedStats.sort(mUidStatsComparator);
 
-        boolean changed = false;
+        final ArraySet<JobStatus> changedJobs = new ArraySet<>();
         // Iterate in reverse order to remove existing callbacks before adding new ones.
         for (int i = mSortedStats.size() - 1; i >= 0; --i) {
             UidStats us = mSortedStats.get(i);
             if (i >= MAX_NETWORK_CALLBACKS) {
-                changed |= unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed);
+                if (unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed)) {
+                    changedJobs.addAll(mTrackedJobs.get(us.uid));
+                }
             } else {
                 UidDefaultNetworkCallback defaultNetworkCallback =
                         mCurrentDefaultNetworkCallbacks.get(us.uid);
@@ -818,8 +869,8 @@
                 }
             }
         }
-        if (changed) {
-            mStateChangedListener.onControllerStateChanged();
+        if (changedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(changedJobs);
         }
     }
 
@@ -942,16 +993,23 @@
      */
     @GuardedBy("mLock")
     private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
-        boolean changed = false;
+        final ArraySet<JobStatus> changedJobs;
         if (filterUid == -1) {
+            changedJobs = new ArraySet<>();
             for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
-                changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork);
+                if (updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork)) {
+                    changedJobs.addAll(mTrackedJobs.valueAt(i));
+                }
             }
         } else {
-            changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork);
+            if (updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork)) {
+                changedJobs = mTrackedJobs.get(filterUid);
+            } else {
+                changedJobs = null;
+            }
         }
-        if (changed) {
-            mStateChangedListener.onControllerStateChanged();
+        if (changedJobs != null && changedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(changedJobs);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index e64233f..83a756c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -331,7 +331,9 @@
             // Let the scheduler know that state has changed. This may or may not result in an
             // execution.
             if (reportChange) {
-                mStateChangedListener.onControllerStateChanged();
+                ArraySet<JobStatus> changedJob = new ArraySet<>();
+                changedJob.add(mJobStatus);
+                mStateChangedListener.onControllerStateChanged(changedJob);
             }
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 79ef321..090260c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -104,14 +104,15 @@
                             Slog.d(TAG, "Got temp whitelist "
                                     + Arrays.toString(mPowerSaveTempWhitelistAppIds));
                         }
-                        boolean changed = false;
+                        final ArraySet<JobStatus> changedJobs = new ArraySet<>();
                         final long nowElapsed = sElapsedRealtimeClock.millis();
                         for (int i = 0; i < mAllowInIdleJobs.size(); i++) {
-                            changed |=
-                                    updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed);
+                            if (updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed)) {
+                                changedJobs.add(mAllowInIdleJobs.valueAt(i));
+                            }
                         }
-                        if (changed) {
-                            mStateChangedListener.onControllerStateChanged();
+                        if (changedJobs.size() > 0) {
+                            mStateChangedListener.onControllerStateChanged(changedJobs);
                         }
                     }
                     break;
@@ -184,8 +185,8 @@
         mForegroundUids.put(uid, active);
         mDeviceIdleUpdateFunctor.prepare();
         mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor);
-        if (mDeviceIdleUpdateFunctor.mChanged) {
-            mStateChangedListener.onControllerStateChanged();
+        if (mDeviceIdleUpdateFunctor.mChangedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(mDeviceIdleUpdateFunctor.mChangedJobs);
         }
     }
 
@@ -283,17 +284,19 @@
     }
 
     final class DeviceIdleUpdateFunctor implements Consumer<JobStatus> {
-        boolean mChanged;
+        final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
         long mUpdateTimeElapsed = 0;
 
         void prepare() {
-            mChanged = false;
+            mChangedJobs.clear();
             mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
         }
 
         @Override
         public void accept(JobStatus jobStatus) {
-            mChanged |= updateTaskStateLocked(jobStatus, mUpdateTimeElapsed);
+            if (updateTaskStateLocked(jobStatus, mUpdateTimeElapsed)) {
+                mChangedJobs.add(jobStatus);
+            }
         }
     }
 
@@ -310,8 +313,9 @@
                     synchronized (mLock) {
                         mDeviceIdleUpdateFunctor.prepare();
                         mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
-                        if (mDeviceIdleUpdateFunctor.mChanged) {
-                            mStateChangedListener.onControllerStateChanged();
+                        if (mDeviceIdleUpdateFunctor.mChangedJobs.size() > 0) {
+                            mStateChangedListener.onControllerStateChanged(
+                                    mDeviceIdleUpdateFunctor.mChangedJobs);
                         }
                     }
                     break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index e26a3c6..a6fae2c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -98,7 +98,7 @@
                 mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle);
             }
         }
-        mStateChangedListener.onControllerStateChanged();
+        mStateChangedListener.onControllerStateChanged(mTrackedTasks);
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 95eb220..fda57f7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -391,6 +391,7 @@
 
     private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
     private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+    private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
 
     /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
     /////// states change.
@@ -531,7 +532,7 @@
 
         mInternalFlags = internalFlags;
 
-        updateEstimatedNetworkBytesLocked();
+        updateNetworkBytesLocked();
 
         if (job.getRequiredNetwork() != null) {
             // Later, when we check if a given network satisfies the required
@@ -664,7 +665,7 @@
                     sourcePackageName, sourceUserId, toShortString()));
         }
         pendingWork.add(work);
-        updateEstimatedNetworkBytesLocked();
+        updateNetworkBytesLocked();
     }
 
     public JobWorkItem dequeueWorkLocked() {
@@ -677,7 +678,7 @@
                 executingWork.add(work);
                 work.bumpDeliveryCount();
             }
-            updateEstimatedNetworkBytesLocked();
+            updateNetworkBytesLocked();
             return work;
         }
         return null;
@@ -736,7 +737,7 @@
             pendingWork = null;
             executingWork = null;
             incomingJob.nextPendingWorkId = nextPendingWorkId;
-            incomingJob.updateEstimatedNetworkBytesLocked();
+            incomingJob.updateNetworkBytesLocked();
         } else {
             // We are completely stopping the job...  need to clean up work.
             ungrantWorkList(pendingWork);
@@ -744,7 +745,7 @@
             ungrantWorkList(executingWork);
             executingWork = null;
         }
-        updateEstimatedNetworkBytesLocked();
+        updateNetworkBytesLocked();
     }
 
     public void prepareLocked() {
@@ -944,9 +945,10 @@
         }
     }
 
-    private void updateEstimatedNetworkBytesLocked() {
+    private void updateNetworkBytesLocked() {
         mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
         mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+        mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
 
         if (pendingWork != null) {
             for (int i = 0; i < pendingWork.size(); i++) {
@@ -968,6 +970,12 @@
                         mTotalNetworkUploadBytes += uploadBytes;
                     }
                 }
+                final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
+                if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+                    mMinimumNetworkChunkBytes = chunkBytes;
+                } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+                    mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes);
+                }
             }
         }
     }
@@ -980,6 +988,10 @@
         return mTotalNetworkUploadBytes;
     }
 
+    public long getMinimumNetworkChunkBytes() {
+        return mMinimumNetworkChunkBytes;
+    }
+
     /** Does this job have any sort of networking constraint? */
     public boolean hasConnectivityConstraint() {
         // No need to check mDynamicConstraints since connectivity will only be in that list if
@@ -1942,6 +1954,10 @@
                 pw.print("Network upload bytes: ");
                 pw.println(mTotalNetworkUploadBytes);
             }
+            if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+                pw.print("Minimum network chunk bytes: ");
+                pw.println(mMinimumNetworkChunkBytes);
+            }
             if (job.getMinLatencyMillis() != 0) {
                 pw.print("Minimum latency: ");
                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 80e9e2d..f7181a4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -1458,7 +1458,8 @@
             final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
             if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)
                     && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
-                mStateChangedListener.onControllerStateChanged();
+                mStateChangedListener
+                        .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
             }
         }
     }
@@ -1578,17 +1579,19 @@
     }
 
     private void maybeUpdateAllConstraintsLocked() {
-        boolean changed = false;
+        final ArraySet<JobStatus> changedJobs = new ArraySet<>();
         final long nowElapsed = sElapsedRealtimeClock.millis();
         for (int u = 0; u < mTrackedJobs.numMaps(); ++u) {
             final int userId = mTrackedJobs.keyAt(u);
             for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) {
                 final String packageName = mTrackedJobs.keyAt(u, p);
-                changed |= maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName);
+                if (maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
+                    changedJobs.addAll(mTrackedJobs.valueAt(u, p));
+                }
             }
         }
-        if (changed) {
-            mStateChangedListener.onControllerStateChanged();
+        if (changedJobs.size() > 0) {
+            mStateChangedListener.onControllerStateChanged(changedJobs);
         }
     }
 
@@ -1646,22 +1649,27 @@
     private class UidConstraintUpdater implements Consumer<JobStatus> {
         private final SparseArrayMap<String, Integer> mToScheduleStartAlarms =
                 new SparseArrayMap<>();
-        public boolean wasJobChanged;
+        public final ArraySet<JobStatus> changedJobs = new ArraySet<>();
         long mUpdateTimeElapsed = 0;
 
         void prepare() {
             mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
+            changedJobs.clear();
         }
 
         @Override
         public void accept(JobStatus jobStatus) {
-            wasJobChanged |= setConstraintSatisfied(
-                    jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus));
+            if (setConstraintSatisfied(
+                    jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus))) {
+                changedJobs.add(jobStatus);
+            }
             final boolean outOfEJQuota;
             if (jobStatus.isRequestedExpeditedJob()) {
                 final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
-                wasJobChanged |= setExpeditedConstraintSatisfied(
-                        jobStatus, mUpdateTimeElapsed, isWithinEJQuota);
+                if (setExpeditedConstraintSatisfied(
+                        jobStatus, mUpdateTimeElapsed, isWithinEJQuota)) {
+                    changedJobs.add(jobStatus);
+                }
                 outOfEJQuota = !isWithinEJQuota;
             } else {
                 outOfEJQuota = false;
@@ -1691,21 +1699,21 @@
         }
 
         void reset() {
-            wasJobChanged = false;
             mToScheduleStartAlarms.clear();
         }
     }
 
     private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater();
 
-    private boolean maybeUpdateConstraintForUidLocked(final int uid) {
+    @GuardedBy("mLock")
+    @NonNull
+    private ArraySet<JobStatus> maybeUpdateConstraintForUidLocked(final int uid) {
         mUpdateUidConstraints.prepare();
         mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints);
 
         mUpdateUidConstraints.postProcess();
-        boolean changed = mUpdateUidConstraints.wasJobChanged;
         mUpdateUidConstraints.reset();
-        return changed;
+        return mUpdateUidConstraints.changedJobs;
     }
 
     /**
@@ -2320,7 +2328,8 @@
                                 nowElapsed, debits, pendingReward)
                                 && maybeUpdateConstraintForPkgLocked(nowElapsed,
                                 mPkg.userId, mPkg.packageName)) {
-                            mStateChangedListener.onControllerStateChanged();
+                            mStateChangedListener.onControllerStateChanged(
+                                    mTrackedJobs.get(mPkg.userId, mPkg.packageName));
                         }
                     }
                     break;
@@ -2425,7 +2434,8 @@
             }
             if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
                     userId, packageName)) {
-                mStateChangedListener.onControllerStateChanged();
+                mStateChangedListener
+                        .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
             }
         }
         if (restrictedChanges.size() > 0) {
@@ -2459,8 +2469,9 @@
                             t.onStateChangedLocked(nowElapsed, true);
                         }
                     }
-                    if (maybeUpdateConstraintForUidLocked(uid)) {
-                        mStateChangedListener.onControllerStateChanged();
+                    final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForUidLocked(uid);
+                    if (changedJobs.size() > 0) {
+                        mStateChangedListener.onControllerStateChanged(changedJobs);
                     }
                 }
             }
@@ -2558,7 +2569,8 @@
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
                             if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
                                     pkg.userId, pkg.packageName)) {
-                                mStateChangedListener.onControllerStateChanged();
+                                mStateChangedListener.onControllerStateChanged(
+                                        mTrackedJobs.get(pkg.userId, pkg.packageName));
                             }
                         } else {
                             // This could potentially happen if an old session phases out while a
@@ -2586,7 +2598,8 @@
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
                             if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
                                     pkg.userId, pkg.packageName)) {
-                                mStateChangedListener.onControllerStateChanged();
+                                mStateChangedListener.onControllerStateChanged(
+                                        mTrackedJobs.get(pkg.userId, pkg.packageName));
                             }
                         } else {
                             // This could potentially happen if an old session phases out while a
@@ -2618,7 +2631,8 @@
                         }
                         if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
                                 userId, packageName)) {
-                            mStateChangedListener.onControllerStateChanged();
+                            mStateChangedListener.onControllerStateChanged(
+                                    mTrackedJobs.get(userId, packageName));
                         }
                         break;
                     }
@@ -2680,8 +2694,10 @@
                                     }
                                 }
                             }
-                            if (maybeUpdateConstraintForUidLocked(uid)) {
-                                mStateChangedListener.onControllerStateChanged();
+                            final ArraySet<JobStatus> changedJobs =
+                                    maybeUpdateConstraintForUidLocked(uid);
+                            if (changedJobs.size() > 0) {
+                                mStateChangedListener.onControllerStateChanged(changedJobs);
                             }
                         }
                         break;
@@ -2760,8 +2776,10 @@
                                         t.onStateChangedLocked(nowElapsed, false);
                                     }
                                 }
-                                if (maybeUpdateConstraintForUidLocked(uid)) {
-                                    mStateChangedListener.onControllerStateChanged();
+                                final ArraySet<JobStatus> changedJobs =
+                                        maybeUpdateConstraintForUidLocked(uid);
+                                if (changedJobs.size() > 0) {
+                                    mStateChangedListener.onControllerStateChanged(changedJobs);
                                 }
                             }
                         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
index 8678913..1ce0a7f6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
@@ -93,7 +93,7 @@
         } else if (reportChange) {
             // Let the scheduler know that state has changed. This may or may not result in an
             // execution.
-            mStateChangedListener.onControllerStateChanged();
+            mStateChangedListener.onControllerStateChanged(mTrackedTasks);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index e8ebfb5..b6361ce 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,12 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
-import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.content.Context;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
@@ -62,8 +61,6 @@
     private long mNextDelayExpiredElapsedMillis;
     private volatile long mLastFiredDelayExpiredElapsedMillis;
 
-    private final boolean mChainedAttributionEnabled;
-
     private AlarmManager mAlarmService = null;
     /** List of tracked jobs, sorted asc. by deadline */
     private final List<JobStatus> mTrackedJobs = new LinkedList<>();
@@ -73,7 +70,6 @@
 
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
-        mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
     }
 
     /**
@@ -117,7 +113,8 @@
             it.add(job);
 
             job.setTrackingController(JobStatus.TRACKING_TIME);
-            WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
+            WorkSource ws =
+                    mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
 
             // Only update alarms if the job would be ready with the relevant timing constraint
             // satisfied.
@@ -165,7 +162,7 @@
             } else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
                 // This job's deadline is earlier than the current set alarm. Update the alarm.
                 setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
-                        deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+                        mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
             }
         }
         if (job.hasTimingDelayConstraint()
@@ -177,7 +174,7 @@
                     && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                 // This job's delay is earlier than the current set alarm. Update the alarm.
                 setDelayExpiredAlarmLocked(job.getEarliestRunTime(),
-                        deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+                        mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
             }
         }
     }
@@ -248,7 +245,7 @@
                 }
             }
             setDeadlineExpiredAlarmLocked(nextExpiryTime,
-                    deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
+                    mService.deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
         }
     }
 
@@ -276,7 +273,7 @@
             long nextDelayTime = Long.MAX_VALUE;
             int nextDelayUid = 0;
             String nextDelayPackageName = null;
-            boolean ready = false;
+            final ArraySet<JobStatus> changedJobs = new ArraySet<>();
             Iterator<JobStatus> it = mTrackedJobs.iterator();
             final long nowElapsedMillis = sElapsedRealtimeClock.millis();
             while (it.hasNext()) {
@@ -288,9 +285,7 @@
                     if (canStopTrackingJobLocked(job)) {
                         it.remove();
                     }
-                    if (job.isReady()) {
-                        ready = true;
-                    }
+                    changedJobs.add(job);
                 } else {
                     if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                         if (DEBUG) {
@@ -308,23 +303,11 @@
                     }
                 }
             }
-            if (ready) {
-                mStateChangedListener.onControllerStateChanged();
+            if (changedJobs.size() > 0) {
+                mStateChangedListener.onControllerStateChanged(changedJobs);
             }
             setDelayExpiredAlarmLocked(nextDelayTime,
-                    deriveWorkSource(nextDelayUid, nextDelayPackageName));
-        }
-    }
-
-    private WorkSource deriveWorkSource(int uid, @Nullable String packageName) {
-        if (mChainedAttributionEnabled) {
-            WorkSource ws = new WorkSource();
-            ws.createWorkChain()
-                    .addNode(uid, packageName)
-                    .addNode(Process.SYSTEM_UID, "JobScheduler");
-            return ws;
-        } else {
-            return packageName == null ? new WorkSource(uid) : new WorkSource(uid, packageName);
+                    mService.deriveWorkSource(nextDelayUid, nextDelayPackageName));
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
index 20df3ea..5193179 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -66,7 +66,8 @@
     public abstract void dumpConstants(IndentingPrintWriter pw);
 
     /** Dump any internal constants the Restriction may have. */
-    public abstract void dumpConstants(ProtoOutputStream proto);
+    public void dumpConstants(ProtoOutputStream proto) {
+    }
 
     /** @return reason code for the Restriction. */
     @JobParameters.StopReason
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index 6d67ee3..a9b0465 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -20,16 +20,21 @@
 import android.os.PowerManager;
 import android.os.PowerManager.OnThermalStatusChangedListener;
 import android.util.IndentingPrintWriter;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerServiceDumpProto;
 import com.android.server.job.controllers.JobStatus;
 
 public class ThermalStatusRestriction extends JobRestriction {
     private static final String TAG = "ThermalStatusRestriction";
 
-    private volatile boolean mIsThermalRestricted = false;
+    /** The threshold at which we start restricting non-EJ jobs. */
+    private static final int REGULAR_JOB_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE;
+    /** The lowest threshold at which we start restricting jobs. */
+    private static final int LOWER_THRESHOLD = REGULAR_JOB_THRESHOLD;
+    /** The threshold at which we start restricting ALL jobs. */
+    private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_CRITICAL;
+
+    private volatile int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
 
     private PowerManager mPowerManager;
 
@@ -47,29 +52,46 @@
             public void onThermalStatusChanged(int status) {
                 // This is called on the main thread. Do not do any slow operations in it.
                 // mService.onControllerStateChanged() will just post a message, which is okay.
-                final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
-                if (mIsThermalRestricted == shouldBeActive) {
-                    return;
+
+                // There are three buckets:
+                //   1. Below the lower threshold (we don't care about changes within this bucket)
+                //   2. Between the lower and upper thresholds.
+                //     -> We care about transitions across buckets
+                //     -> We care about transitions within the middle bucket
+                //   3. Upper the upper threshold (we don't care about changes within this bucket)
+                final boolean significantChange =
+                        // Handle transitions within and into the bucket we care about (thus
+                        // causing us to change our restrictions).
+                        (status >= LOWER_THRESHOLD && status <= UPPER_THRESHOLD)
+                                // Take care of transitions from the 2nd or 3rd bucket to the 1st
+                                // bucket (thus exiting any restrictions we started enforcing).
+                                || (mThermalStatus >= LOWER_THRESHOLD && status < LOWER_THRESHOLD)
+                                // Take care of transitions from the 1st or 2nd bucket to the 3rd
+                                // bucket (thus resulting in us beginning to enforce the tightest
+                                // restrictions).
+                                || (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD);
+                mThermalStatus = status;
+                if (significantChange) {
+                    mService.onControllerStateChanged(null);
                 }
-                mIsThermalRestricted = shouldBeActive;
-                mService.onControllerStateChanged();
             }
         });
     }
 
     @Override
     public boolean isJobRestricted(JobStatus job) {
-        return mIsThermalRestricted;
+        if (mThermalStatus >= UPPER_THRESHOLD) {
+            return true;
+        }
+        if (mThermalStatus >= REGULAR_JOB_THRESHOLD) {
+            return !job.shouldTreatAsExpeditedJob();
+        }
+        return false;
     }
 
     @Override
     public void dumpConstants(IndentingPrintWriter pw) {
-        pw.print("In thermal throttling?: ");
-        pw.println(mIsThermalRestricted);
-    }
-
-    @Override
-    public void dumpConstants(ProtoOutputStream proto) {
-        proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
+        pw.print("Thermal status: ");
+        pw.println(mThermalStatus);
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
new file mode 100644
index 0000000..7f09e71
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
+import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
+import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
+import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
+import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
+import static com.android.server.tare.EconomicPolicy.eventToString;
+import static com.android.server.tare.EconomicPolicy.getEventType;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.usage.AppStandbyInternal;
+
+import libcore.util.EmptyArray;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.function.Consumer;
+
+/**
+ * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with
+ * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or
+ * replenishing balances, and tracking ongoing events.
+ */
+class Agent {
+    private static final String TAG = "TARE-" + Agent.class.getSimpleName();
+    private static final boolean DEBUG = InternalResourceService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * The minimum amount of time an app must not have been used by the user before we start
+     * regularly reclaiming ARCs from it.
+     */
+    private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
+    /**
+     * The maximum amount of time we'll keep a transaction around for.
+     * For now, only keep transactions we actually have a use for. We can increase it if we want
+     * to use older transactions or provide older transactions to apps.
+     */
+    private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
+
+    private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*";
+    private static final String ALARM_TAG_LEDGER_CLEANUP = "*tare.ledger_cleanup*";
+
+    private final Object mLock;
+    private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+    private final Handler mHandler;
+    private final InternalResourceService mIrs;
+
+    private final AppStandbyInternal mAppStandbyInternal;
+
+    @GuardedBy("mLock")
+    private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+
+    @GuardedBy("mLock")
+    private final SparseArrayMap<String, SparseArrayMap<String, OngoingEvent>>
+            mCurrentOngoingEvents = new SparseArrayMap<>();
+
+    /**
+     * Set of {@link ActionAffordabilityNote}s keyed by userId-pkgName.
+     *
+     * Note: it would be nice/better to sort by base price since that doesn't change and simply
+     * look at the change in the "insertion" of what would be affordable, but since CTP
+     * is factored into the final price, the sorting order (by modified price) could be different
+     * and that method wouldn't work >:(
+     */
+    @GuardedBy("mLock")
+    private final SparseArrayMap<String, ArraySet<ActionAffordabilityNote>>
+            mActionAffordabilityNotes = new SparseArrayMap<>();
+
+    @GuardedBy("mLock")
+    private long mCurrentNarcsInCirculation;
+
+    /**
+     * Listener to track and manage when we remove old transactions from ledgers.
+     */
+    @GuardedBy("mLock")
+    private final LedgerCleanupAlarmListener mLedgerCleanupAlarmListener =
+            new LedgerCleanupAlarmListener();
+
+    /**
+     * Listener to track and manage when apps will cross the closest affordability threshold (in
+     * both directions).
+     */
+    @GuardedBy("mLock")
+    private final BalanceThresholdAlarmListener mBalanceThresholdAlarmListener =
+            new BalanceThresholdAlarmListener();
+
+    private static final int MSG_CHECK_BALANCE = 0;
+    private static final int MSG_CLEAN_LEDGER = 1;
+    private static final int MSG_SET_ALARMS = 2;
+
+    Agent(@NonNull InternalResourceService irs,
+            @NonNull CompleteEconomicPolicy completeEconomicPolicy) {
+        mLock = irs.getLock();
+        mIrs = irs;
+        mCompleteEconomicPolicy = completeEconomicPolicy;
+        mHandler = new AgentHandler(TareHandlerThread.get().getLooper());
+        mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    private Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
+        Ledger ledger = mLedgers.get(userId, pkgName);
+        if (ledger == null) {
+            // TODO: load from disk
+            ledger = new Ledger();
+            mLedgers.add(userId, pkgName, ledger);
+        }
+        return ledger;
+    }
+
+    private class TotalDeltaCalculator implements Consumer<OngoingEvent> {
+        private Ledger mLedger;
+        private long mNowElapsed;
+        private long mNow;
+        private long mTotal;
+
+        void reset(@NonNull Ledger ledger, long nowElapsed, long now) {
+            mLedger = ledger;
+            mNowElapsed = nowElapsed;
+            mNow = now;
+            mTotal = 0;
+        }
+
+        @Override
+        public void accept(OngoingEvent ongoingEvent) {
+            mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private final TotalDeltaCalculator mTotalDeltaCalculator = new TotalDeltaCalculator();
+
+    /** Get an app's current balance, factoring in any currently ongoing events. */
+    @GuardedBy("mLock")
+    long getBalanceLocked(final int userId, @NonNull final String pkgName) {
+        final Ledger ledger = getLedgerLocked(userId, pkgName);
+        long balance = ledger.getCurrentBalance();
+        SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                mCurrentOngoingEvents.get(userId, pkgName);
+        if (ongoingEvents != null) {
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            final long now = System.currentTimeMillis();
+            mTotalDeltaCalculator.reset(ledger, nowElapsed, now);
+            ongoingEvents.forEach(mTotalDeltaCalculator);
+            balance += mTotalDeltaCalculator.mTotal;
+        }
+        return balance;
+    }
+
+    @GuardedBy("mLock")
+    void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
+            final int eventId, @Nullable String tag) {
+        final long now = System.currentTimeMillis();
+        final Ledger ledger = getLedgerLocked(userId, pkgName);
+        final boolean wasSolvent = getBalanceLocked(userId, pkgName) > 0;
+
+        final int eventType = getEventType(eventId);
+        switch (eventType) {
+            case TYPE_ACTION:
+                final long actionCost =
+                        mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+                recordTransactionLocked(userId, pkgName, ledger,
+                        new Ledger.Transaction(now, now, eventId, tag, -actionCost), true);
+                break;
+
+            case TYPE_REWARD:
+                final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+                if (reward != null) {
+                    final long rewardSum = ledger.get24HourSum(eventId, now);
+                    final long rewardVal = Math.max(0,
+                            Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
+                    recordTransactionLocked(userId, pkgName, ledger,
+                            new Ledger.Transaction(now, now, eventId, tag, rewardVal), true);
+                }
+                break;
+
+            default:
+                Slog.w(TAG, "Unsupported event type: " + eventType);
+        }
+        scheduleBalanceCheckLocked(userId, pkgName);
+    }
+
+    @GuardedBy("mLock")
+    void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
+            @Nullable String tag, final long startElapsed) {
+        noteOngoingEventLocked(userId, pkgName, eventId, tag, startElapsed, true);
+    }
+
+    @GuardedBy("mLock")
+    void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
+            @Nullable String tag, final long startElapsed, final boolean updateBalanceCheck) {
+        SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                mCurrentOngoingEvents.get(userId, pkgName);
+        if (ongoingEvents == null) {
+            ongoingEvents = new SparseArrayMap<>();
+            mCurrentOngoingEvents.add(userId, pkgName, ongoingEvents);
+        }
+        OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+
+        final int eventType = getEventType(eventId);
+        switch (eventType) {
+            case TYPE_ACTION:
+                final long actionCost =
+                        mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+                if (ongoingEvent == null) {
+                    ongoingEvents.add(eventId, tag,
+                            new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+                } else {
+                    ongoingEvent.refCount++;
+                }
+                break;
+
+            case TYPE_REWARD:
+                final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+                if (reward != null) {
+                    if (ongoingEvent == null) {
+                        ongoingEvents.add(eventId, tag, new OngoingEvent(
+                                eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+                    } else {
+                        ongoingEvent.refCount++;
+                    }
+                }
+                break;
+
+            default:
+                Slog.w(TAG, "Unsupported event type: " + eventType);
+        }
+
+        if (updateBalanceCheck) {
+            scheduleBalanceCheckLocked(userId, pkgName);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void onDeviceStateChangedLocked() {
+        final long now = System.currentTimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+
+        mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
+            final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                    mActionAffordabilityNotes.get(userId, pkgName);
+            final boolean[] wasAffordable;
+            if (actionAffordabilityNotes != null) {
+                final int size = actionAffordabilityNotes.size();
+                wasAffordable = new boolean[size];
+                for (int i = 0; i < size; ++i) {
+                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+                    final long originalBalance =
+                            getLedgerLocked(userId, pkgName).getCurrentBalance();
+                    wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
+                }
+            } else {
+                wasAffordable = EmptyArray.BOOLEAN;
+            }
+            ongoingEvents.forEach((ongoingEvent) -> {
+                // Disable balance check & affordability notifications here because we're in the
+                // middle of updating ongoing action costs/prices and sending out notifications
+                // or rescheduling the balance check alarm would be a waste since we'll have to
+                // redo them again after all of our internal state is updated.
+                stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+                        ongoingEvent.tag, nowElapsed, now, false, false);
+                noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+                        nowElapsed, false);
+            });
+            if (actionAffordabilityNotes != null) {
+                final int size = actionAffordabilityNotes.size();
+                for (int i = 0; i < size; ++i) {
+                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+                    note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+                    final long newBalance = getLedgerLocked(userId, pkgName).getCurrentBalance();
+                    final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+                    if (wasAffordable[i] != isAffordable) {
+                        note.setNewAffordability(isAffordable);
+                        mIrs.postAffordabilityChanged(userId, pkgName, note);
+                    }
+                }
+            }
+            scheduleBalanceCheckLocked(userId, pkgName);
+        });
+    }
+
+    @GuardedBy("mLock")
+    void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
+        final long now = System.currentTimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+
+        for (int i = 0; i < pkgNames.size(); ++i) {
+            final String pkgName = pkgNames.valueAt(i);
+            SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                    mCurrentOngoingEvents.get(userId, pkgName);
+            if (ongoingEvents != null) {
+                final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                        mActionAffordabilityNotes.get(userId, pkgName);
+                final boolean[] wasAffordable;
+                if (actionAffordabilityNotes != null) {
+                    final int size = actionAffordabilityNotes.size();
+                    wasAffordable = new boolean[size];
+                    for (int n = 0; n < size; ++n) {
+                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+                        final long originalBalance =
+                                getLedgerLocked(userId, pkgName).getCurrentBalance();
+                        wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
+                    }
+                } else {
+                    wasAffordable = EmptyArray.BOOLEAN;
+                }
+                ongoingEvents.forEach((ongoingEvent) -> {
+                    // Disable balance check & affordability notifications here because we're in the
+                    // middle of updating ongoing action costs/prices and sending out notifications
+                    // or rescheduling the balance check alarm would be a waste since we'll have to
+                    // redo them again after all of our internal state is updated.
+                    stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+                            ongoingEvent.tag, nowElapsed, now, false, false);
+                    noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+                            nowElapsed, false);
+                });
+                if (actionAffordabilityNotes != null) {
+                    final int size = actionAffordabilityNotes.size();
+                    for (int n = 0; n < size; ++n) {
+                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+                        note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+                        final long newBalance =
+                                getLedgerLocked(userId, pkgName).getCurrentBalance();
+                        final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+                        if (wasAffordable[n] != isAffordable) {
+                            note.setNewAffordability(isAffordable);
+                            mIrs.postAffordabilityChanged(userId, pkgName, note);
+                        }
+                    }
+                }
+                scheduleBalanceCheckLocked(userId, pkgName);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
+            @Nullable String tag, final long nowElapsed, final long now) {
+        stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true, true);
+    }
+
+    /**
+     * @param updateBalanceCheck          Whether or not to reschedule the affordability/balance
+     *                                    check alarm.
+     * @param notifyOnAffordabilityChange Whether or not to evaluate the app's ability to afford
+     *                                    registered bills and notify listeners about any changes.
+     */
+    @GuardedBy("mLock")
+    void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
+            @Nullable String tag, final long nowElapsed, final long now,
+            final boolean updateBalanceCheck, final boolean notifyOnAffordabilityChange) {
+        final Ledger ledger = getLedgerLocked(userId, pkgName);
+
+        SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                mCurrentOngoingEvents.get(userId, pkgName);
+        if (ongoingEvents == null) {
+            Slog.wtf(TAG, "No ongoing transactions :/");
+            return;
+        }
+        final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+        if (ongoingEvent == null) {
+            Slog.wtf(TAG, "Nonexistent ongoing transaction "
+                    + eventToString(eventId) + (tag == null ? "" : ":" + tag)
+                    + " for <" + userId + ">" + pkgName + " ended");
+            return;
+        }
+        ongoingEvent.refCount--;
+        if (ongoingEvent.refCount <= 0) {
+            final long startElapsed = ongoingEvent.startTimeElapsed;
+            final long startTime = now - (nowElapsed - startElapsed);
+            final long actualDelta = getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+            recordTransactionLocked(userId, pkgName, ledger,
+                    new Ledger.Transaction(startTime, now, eventId, tag, actualDelta),
+                    notifyOnAffordabilityChange);
+            ongoingEvents.delete(eventId, tag);
+        }
+        if (updateBalanceCheck) {
+            scheduleBalanceCheckLocked(userId, pkgName);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
+            long nowElapsed, long now) {
+        final long startElapsed = ongoingEvent.startTimeElapsed;
+        final long durationSecs = (nowElapsed - startElapsed) / 1000;
+        final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+        if (ongoingEvent.reward == null) {
+            return computedDelta;
+        }
+        final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
+        return Math.max(0,
+                Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+    }
+
+    @GuardedBy("mLock")
+    private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+            @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction,
+            final boolean notifyOnAffordabilityChange) {
+        final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
+        final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
+        if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
+            final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+            Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+                    + eventToString(transaction.eventId)
+                    + (transaction.tag == null ? "" : ":" + transaction.tag)
+                    + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+            transaction = new Ledger.Transaction(
+                    transaction.startTimeMs, transaction.endTimeMs,
+                    transaction.eventId, transaction.tag, newDelta);
+        }
+        final long originalBalance = ledger.getCurrentBalance();
+        if (transaction.delta > 0
+                && originalBalance + transaction.delta
+                > mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
+            final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+            Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
+                    + eventToString(transaction.eventId)
+                    + (transaction.tag == null ? "" : ":" + transaction.tag)
+                    + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+            transaction = new Ledger.Transaction(
+                    transaction.startTimeMs, transaction.endTimeMs,
+                    transaction.eventId, transaction.tag, newDelta);
+        }
+        ledger.recordTransaction(transaction);
+        mCurrentNarcsInCirculation += transaction.delta;
+        if (!mLedgerCleanupAlarmListener.hasAlarmScheduledLocked(userId, pkgName)) {
+            // The earliest transaction won't change until we clean up the ledger, so no point
+            // continuing to reschedule an existing cleanup.
+            final long cleanupAlarmElapsed = SystemClock.elapsedRealtime() + MAX_TRANSACTION_AGE_MS
+                    - (System.currentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
+            mLedgerCleanupAlarmListener.addAlarmLocked(userId, pkgName, cleanupAlarmElapsed);
+        }
+        // TODO: save changes to disk in a background thread
+        if (notifyOnAffordabilityChange) {
+            final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                    mActionAffordabilityNotes.get(userId, pkgName);
+            if (actionAffordabilityNotes != null) {
+                final long newBalance = ledger.getCurrentBalance();
+                for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+                    final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+                    if (note.isCurrentlyAffordable() != isAffordable) {
+                        note.setNewAffordability(isAffordable);
+                        mIrs.postAffordabilityChanged(userId, pkgName, note);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The
+     * reclamation will not reduce an app's balance below its minimum balance as dictated by the
+     * EconomicPolicy.
+     *
+     * @param percentage A value between 0 and 1 to indicate how much of the unused balance should
+     *                   be reclaimed.
+     */
+    @GuardedBy("mLock")
+    void reclaimUnusedAssetsLocked(double percentage) {
+        final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+        final long now = System.currentTimeMillis();
+        for (int i = 0; i < pkgs.size(); ++i) {
+            final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
+            final String pkgName = pkgs.get(i).packageName;
+            final Ledger ledger = getLedgerLocked(userId, pkgName);
+            // AppStandby only counts elapsed time for things like this
+            // TODO: should we use clock time instead?
+            final long timeSinceLastUsedMs =
+                    mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+            if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) {
+                // Use a constant floor instead of the scaled floor from the IRS.
+                final long minBalance =
+                        mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName);
+                final long curBalance = ledger.getCurrentBalance();
+                long toReclaim = (long) (curBalance * percentage);
+                if (curBalance - toReclaim < minBalance) {
+                    toReclaim = curBalance - minBalance;
+                }
+                if (toReclaim > 0) {
+                    Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
+                            + " from <" + userId + ">" + pkgName);
+
+                    recordTransactionLocked(userId, pkgName, ledger,
+                            new Ledger.Transaction(
+                                    now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+                            true);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    void distributeBasicIncomeLocked(int batteryLevel) {
+        List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+        final long now = System.currentTimeMillis();
+        for (int i = 0; i < pkgs.size(); ++i) {
+            final PackageInfo pkgInfo = pkgs.get(i);
+            final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
+            final String pkgName = pkgInfo.packageName;
+            Ledger ledger = getLedgerLocked(userId, pkgName);
+            final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+            final double perc = batteryLevel / 100d;
+            // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
+            if (ledger.getCurrentBalance() < minBalance) {
+                final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+                recordTransactionLocked(userId, pkgName, ledger,
+                        new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
+                                null, (long) (perc * shortfall)), true);
+            }
+        }
+    }
+
+    /** Give each app an initial balance. */
+    @GuardedBy("mLock")
+    void grantBirthrightsLocked() {
+        UserManagerInternal userManagerInternal =
+                LocalServices.getService(UserManagerInternal.class);
+        final int[] userIds = userManagerInternal.getUserIds();
+        for (int userId : userIds) {
+            grantBirthrightsLocked(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void grantBirthrightsLocked(final int userId) {
+        PackageManager packageManager = mIrs.getContext().getPackageManager();
+        List<PackageInfo> pkgs = packageManager.getInstalledPackagesAsUser(0, userId);
+        final long maxBirthright =
+                mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
+        final long now = System.currentTimeMillis();
+
+        for (int i = 0; i < pkgs.size(); ++i) {
+            final PackageInfo packageInfo = pkgs.get(i);
+            final String pkgName = packageInfo.packageName;
+            final Ledger ledger = getLedgerLocked(userId, pkgName);
+            if (ledger.getCurrentBalance() > 0) {
+                // App already got credits somehow. Move along.
+                Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up");
+                continue;
+            }
+
+            recordTransactionLocked(userId, pkgName, ledger,
+                    new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+                            Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))),
+                    true);
+        }
+    }
+
+    @GuardedBy("mLock")
+    void grantBirthrightLocked(final int userId, @NonNull final String pkgName) {
+        final Ledger ledger = getLedgerLocked(userId, pkgName);
+        if (ledger.getCurrentBalance() > 0) {
+            Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed");
+            // App already got credits somehow. Move along.
+            return;
+        }
+
+        List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+        final int numPackages = pkgs.size();
+        final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
+        final long now = System.currentTimeMillis();
+
+        recordTransactionLocked(userId, pkgName, ledger,
+                new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+                        Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))), true);
+    }
+
+    @GuardedBy("mLock")
+    void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
+        reclaimAssetsLocked(userId, pkgName);
+        mLedgerCleanupAlarmListener.removeAlarmLocked(userId, pkgName);
+        mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+    }
+
+    /**
+     * Reclaims any ARCs granted to the app, making them available to other apps. Also deletes the
+     * app's ledger and stops any ongoing event tracking.
+     */
+    @GuardedBy("mLock")
+    private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
+        Ledger ledger = getLedgerLocked(userId, pkgName);
+        if (ledger.getCurrentBalance() != 0) {
+            mCurrentNarcsInCirculation -= ledger.getCurrentBalance();
+        }
+        // TODO: delete ledger entry from disk
+        mLedgers.delete(userId, pkgName);
+        mCurrentOngoingEvents.delete(userId, pkgName);
+    }
+
+    @GuardedBy("mLock")
+    void onUserRemovedLocked(final int userId, @NonNull final List<String> pkgNames) {
+        reclaimAssetsLocked(userId, pkgNames);
+        mLedgerCleanupAlarmListener.removeAlarmsLocked(userId);
+        mBalanceThresholdAlarmListener.removeAlarmsLocked(userId);
+    }
+
+    @GuardedBy("mLock")
+    private void reclaimAssetsLocked(final int userId, @NonNull final List<String> pkgNames) {
+        for (int i = 0; i < pkgNames.size(); ++i) {
+            reclaimAssetsLocked(userId, pkgNames.get(i));
+        }
+    }
+
+    @VisibleForTesting
+    static class TrendCalculator implements Consumer<OngoingEvent> {
+        static final long WILL_NOT_CROSS_THRESHOLD = -1;
+
+        private long mCurBalance;
+        /**
+         * The maximum change in credits per second towards the upper threshold
+         * {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never
+         * result in the app crossing the upper threshold.
+         */
+        private long mMaxDeltaPerSecToUpperThreshold;
+        /**
+         * The maximum change in credits per second towards the lower threshold
+         * {@link #mLowerThreshold}. A value of 0 means the current ongoing events will never
+         * result in the app crossing the lower threshold.
+         */
+        private long mMaxDeltaPerSecToLowerThreshold;
+        private long mUpperThreshold;
+        private long mLowerThreshold;
+
+        void reset(long curBalance,
+                @Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) {
+            mCurBalance = curBalance;
+            mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0;
+            mUpperThreshold = Long.MIN_VALUE;
+            mLowerThreshold = Long.MAX_VALUE;
+            if (actionAffordabilityNotes != null) {
+                for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+                    final long price = note.getCachedModifiedPrice();
+                    if (price <= mCurBalance) {
+                        mLowerThreshold = (mLowerThreshold == Long.MAX_VALUE)
+                                ? price : Math.max(mLowerThreshold, price);
+                    } else {
+                        mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
+                                ? price : Math.min(mUpperThreshold, price);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Returns the amount of time (in millisecond) it will take for the app to cross the next
+         * lowest action affordability note (compared to its current balance) based on current
+         * ongoing events.
+         * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the lowest
+         * threshold.
+         */
+        long getTimeToCrossLowerThresholdMs() {
+            if (mMaxDeltaPerSecToLowerThreshold == 0) {
+                // Will never cross upper threshold based on current events.
+                return WILL_NOT_CROSS_THRESHOLD;
+            }
+            // deltaPerSec is a negative value, so do threshold-balance to cancel out the negative.
+            final long minSeconds =
+                    (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+            return minSeconds * 1000;
+        }
+
+        /**
+         * Returns the amount of time (in millisecond) it will take for the app to cross the next
+         * highest action affordability note (compared to its current balance) based on current
+         * ongoing events.
+         * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the upper
+         * threshold.
+         */
+        long getTimeToCrossUpperThresholdMs() {
+            if (mMaxDeltaPerSecToUpperThreshold == 0) {
+                // Will never cross upper threshold based on current events.
+                return WILL_NOT_CROSS_THRESHOLD;
+            }
+            final long minSeconds =
+                    (mUpperThreshold - mCurBalance) / mMaxDeltaPerSecToUpperThreshold;
+            return minSeconds * 1000;
+        }
+
+        @Override
+        public void accept(OngoingEvent ongoingEvent) {
+            if (mCurBalance >= mLowerThreshold && ongoingEvent.deltaPerSec < 0) {
+                mMaxDeltaPerSecToLowerThreshold += ongoingEvent.deltaPerSec;
+            } else if (mCurBalance < mUpperThreshold && ongoingEvent.deltaPerSec > 0) {
+                mMaxDeltaPerSecToUpperThreshold += ongoingEvent.deltaPerSec;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private final TrendCalculator mTrendCalculator = new TrendCalculator();
+
+    @GuardedBy("mLock")
+    private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) {
+        SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                mCurrentOngoingEvents.get(userId, pkgName);
+        if (ongoingEvents == null) {
+            // No ongoing transactions. No reason to schedule
+            mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+            return;
+        }
+        mTrendCalculator.reset(
+                getBalanceLocked(userId, pkgName), mActionAffordabilityNotes.get(userId, pkgName));
+        ongoingEvents.forEach(mTrendCalculator);
+        final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
+        final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs();
+        final long timeToThresholdMs;
+        if (lowerTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) {
+            if (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) {
+                // Will never cross a threshold based on current events.
+                mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+                return;
+            }
+            timeToThresholdMs = upperTimeMs;
+        } else {
+            timeToThresholdMs = (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD)
+                    ? lowerTimeMs : Math.min(lowerTimeMs, upperTimeMs);
+        }
+        mBalanceThresholdAlarmListener.addAlarmLocked(userId, pkgName,
+                SystemClock.elapsedRealtime() + timeToThresholdMs);
+    }
+
+    @VisibleForTesting
+    static class OngoingEvent {
+        public final long startTimeElapsed;
+        public final int eventId;
+        @Nullable
+        public final String tag;
+        @Nullable
+        public final EconomicPolicy.Reward reward;
+        public final long deltaPerSec;
+        public int refCount;
+
+        OngoingEvent(int eventId, @Nullable String tag,
+                @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+            this.startTimeElapsed = startTimeElapsed;
+            this.eventId = eventId;
+            this.tag = tag;
+            this.reward = reward;
+            this.deltaPerSec = deltaPerSec;
+            refCount = 1;
+        }
+    }
+
+    /**
+     * An {@link AlarmManager.OnAlarmListener} that will queue up all pending alarms and only
+     * schedule one alarm for the earliest alarm.
+     */
+    private abstract class AlarmQueueListener implements AlarmManager.OnAlarmListener {
+        final class Package {
+            public final String packageName;
+            public final int userId;
+
+            Package(int userId, String packageName) {
+                this.userId = userId;
+                this.packageName = packageName;
+            }
+
+            @Override
+            public String toString() {
+                return "<" + userId + ">" + packageName;
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                if (obj == null) {
+                    return false;
+                }
+                if (this == obj) {
+                    return true;
+                }
+                if (obj instanceof Package) {
+                    Package other = (Package) obj;
+                    return userId == other.userId && Objects.equals(packageName, other.packageName);
+                } else {
+                    return false;
+                }
+            }
+
+            @Override
+            public int hashCode() {
+                return packageName.hashCode() + userId;
+            }
+        }
+
+        class AlarmQueue extends PriorityQueue<Pair<Package, Long>> {
+            AlarmQueue() {
+                super(1, (o1, o2) -> (int) (o1.second - o2.second));
+            }
+
+            boolean contains(@NonNull Package pkg) {
+                Pair[] alarms = toArray(new Pair[size()]);
+                for (int i = alarms.length - 1; i >= 0; --i) {
+                    if (pkg.equals(alarms[i].first)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            /**
+             * Remove any instances of the Package from the queue.
+             *
+             * @return true if an instance was removed, false otherwise.
+             */
+            boolean remove(@NonNull Package pkg) {
+                boolean removed = false;
+                Pair[] alarms = toArray(new Pair[size()]);
+                for (int i = alarms.length - 1; i >= 0; --i) {
+                    if (pkg.equals(alarms[i].first)) {
+                        remove(alarms[i]);
+                        removed = true;
+                    }
+                }
+                return removed;
+            }
+        }
+
+        @GuardedBy("mLock")
+        private final AlarmQueue mAlarmQueue = new AlarmQueue();
+        private final String mAlarmTag;
+        /** Whether to use an exact alarm or an inexact alarm. */
+        private final boolean mExactAlarm;
+        /** The minimum amount of time between check alarms. */
+        private final long mMinTimeBetweenAlarmsMs;
+        /** The next time the alarm is set to go off, in the elapsed realtime timebase. */
+        @GuardedBy("mLock")
+        private long mTriggerTimeElapsed = 0;
+
+        protected AlarmQueueListener(@NonNull String alarmTag, boolean exactAlarm,
+                long minTimeBetweenAlarmsMs) {
+            mAlarmTag = alarmTag;
+            mExactAlarm = exactAlarm;
+            mMinTimeBetweenAlarmsMs = minTimeBetweenAlarmsMs;
+        }
+
+        @GuardedBy("mLock")
+        boolean hasAlarmScheduledLocked(int userId, @NonNull String pkgName) {
+            final Package pkg = new Package(userId, pkgName);
+            return mAlarmQueue.contains(pkg);
+        }
+
+        @GuardedBy("mLock")
+        void addAlarmLocked(int userId, @NonNull String pkgName, long alarmTimeElapsed) {
+            final Package pkg = new Package(userId, pkgName);
+            mAlarmQueue.remove(pkg);
+            mAlarmQueue.offer(new Pair<>(pkg, alarmTimeElapsed));
+            setNextAlarmLocked();
+        }
+
+        @GuardedBy("mLock")
+        void removeAlarmLocked(@NonNull Package pkg) {
+            if (mAlarmQueue.remove(pkg)) {
+                setNextAlarmLocked();
+            }
+        }
+
+        @GuardedBy("mLock")
+        void removeAlarmLocked(int userId, @NonNull String packageName) {
+            removeAlarmLocked(new Package(userId, packageName));
+        }
+
+        @GuardedBy("mLock")
+        void removeAlarmsLocked(int userId) {
+            boolean removed = false;
+            Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+            for (int i = alarms.length - 1; i >= 0; --i) {
+                final Package pkg = (Package) alarms[i].first;
+                if (userId == pkg.userId) {
+                    mAlarmQueue.remove(alarms[i]);
+                    removed = true;
+                }
+            }
+            if (removed) {
+                setNextAlarmLocked();
+            }
+        }
+
+        /** Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue. */
+        @GuardedBy("mLock")
+        void setNextAlarmLocked() {
+            setNextAlarmLocked(SystemClock.elapsedRealtime());
+        }
+
+        /**
+         * Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue, using
+         * {@code earliestTriggerElapsed} as a floor.
+         */
+        @GuardedBy("mLock")
+        private void setNextAlarmLocked(long earliestTriggerElapsed) {
+            if (mAlarmQueue.size() > 0) {
+                final Pair<Package, Long> alarm = mAlarmQueue.peek();
+                final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, alarm.second);
+                // Only schedule the alarm if one of the following is true:
+                // 1. There isn't one currently scheduled
+                // 2. The new alarm is significantly earlier than the previous alarm. If it's
+                // earlier but not significantly so, then we essentially delay the check for some
+                // apps by up to a minute.
+                // 3. The alarm is after the current alarm.
+                if (mTriggerTimeElapsed == 0
+                        || nextTriggerTimeElapsed < mTriggerTimeElapsed - MINUTE_IN_MILLIS
+                        || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed
+                                + " for app " + alarm.first);
+                    }
+                    mHandler.post(() -> {
+                        // Never call out to AlarmManager with the lock held. This sits below AM.
+                        AlarmManager alarmManager =
+                                mIrs.getContext().getSystemService(AlarmManager.class);
+                        if (alarmManager != null) {
+                            if (mExactAlarm) {
+                                alarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
+                                        nextTriggerTimeElapsed, mAlarmTag, this, mHandler);
+                            } else {
+                                alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+                                        nextTriggerTimeElapsed, mMinTimeBetweenAlarmsMs / 2,
+                                        mAlarmTag, this, mHandler);
+                            }
+                        } else {
+                            mHandler.sendEmptyMessageDelayed(MSG_SET_ALARMS, 30_000);
+                        }
+                    });
+                    mTriggerTimeElapsed = nextTriggerTimeElapsed;
+                }
+            } else {
+                mHandler.post(() -> {
+                    // Never call out to AlarmManager with the lock held. This sits below AM.
+                    AlarmManager alarmManager =
+                            mIrs.getContext().getSystemService(AlarmManager.class);
+                    if (alarmManager != null) {
+                        // This should only be null at boot time. No concerns around not
+                        // cancelling if we get null here.
+                        alarmManager.cancel(this);
+                    }
+                });
+                mTriggerTimeElapsed = 0;
+            }
+        }
+
+        @GuardedBy("mLock")
+        protected abstract void processExpiredAlarmLocked(int userId, @NonNull String packageName);
+
+        @Override
+        public void onAlarm() {
+            synchronized (mLock) {
+                final long nowElapsed = SystemClock.elapsedRealtime();
+                while (mAlarmQueue.size() > 0) {
+                    final Pair<Package, Long> alarm = mAlarmQueue.peek();
+                    if (alarm.second <= nowElapsed) {
+                        processExpiredAlarmLocked(alarm.first.userId, alarm.first.packageName);
+                        mAlarmQueue.remove(alarm);
+                    } else {
+                        break;
+                    }
+                }
+                setNextAlarmLocked(nowElapsed + mMinTimeBetweenAlarmsMs);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dumpLocked(IndentingPrintWriter pw) {
+            pw.print(mAlarmTag);
+            pw.println(" alarms:");
+            pw.increaseIndent();
+
+            if (mAlarmQueue.size() == 0) {
+                pw.println("NOT WAITING");
+            } else {
+                Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+                for (int i = 0; i < alarms.length; ++i) {
+                    final Package pkg = (Package) alarms[i].first;
+                    pw.print(pkg);
+                    pw.print(": ");
+                    pw.print(alarms[i].second);
+                    pw.println();
+                }
+            }
+
+            pw.decreaseIndent();
+        }
+    }
+
+    /** Clean up old transactions from {@link Ledger}s. */
+    private class LedgerCleanupAlarmListener extends AlarmQueueListener {
+        private LedgerCleanupAlarmListener() {
+            // We don't need to run cleanup too frequently.
+            super(ALARM_TAG_LEDGER_CLEANUP, false, HOUR_IN_MILLIS);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+            mHandler.obtainMessage(MSG_CLEAN_LEDGER, userId, 0, packageName).sendToTarget();
+        }
+    }
+
+    /** Track when apps will cross the closest affordability threshold (in both directions). */
+    private class BalanceThresholdAlarmListener extends AlarmQueueListener {
+        private BalanceThresholdAlarmListener() {
+            super(ALARM_TAG_AFFORDABILITY_CHECK, true, 15_000L);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+            mHandler.obtainMessage(MSG_CHECK_BALANCE, userId, 0, packageName).sendToTarget();
+        }
+    }
+
+    @GuardedBy("mLock")
+    public void registerAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName,
+            @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+            @NonNull EconomyManagerInternal.ActionBill bill) {
+        ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                mActionAffordabilityNotes.get(userId, pkgName);
+        if (actionAffordabilityNotes == null) {
+            actionAffordabilityNotes = new ArraySet<>();
+            mActionAffordabilityNotes.add(userId, pkgName, actionAffordabilityNotes);
+        }
+        final ActionAffordabilityNote note =
+                new ActionAffordabilityNote(bill, listener, mCompleteEconomicPolicy);
+        if (actionAffordabilityNotes.add(note)) {
+            note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+            note.setNewAffordability(
+                    getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice());
+            mIrs.postAffordabilityChanged(userId, pkgName, note);
+            // Update ongoing alarm
+            scheduleBalanceCheckLocked(userId, pkgName);
+        }
+    }
+
+    @GuardedBy("mLock")
+    public void unregisterAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName,
+            @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+            @NonNull EconomyManagerInternal.ActionBill bill) {
+        final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                mActionAffordabilityNotes.get(userId, pkgName);
+        if (actionAffordabilityNotes != null) {
+            final ActionAffordabilityNote note =
+                    new ActionAffordabilityNote(bill, listener, mCompleteEconomicPolicy);
+            if (actionAffordabilityNotes.remove(note)) {
+                // Update ongoing alarm
+                scheduleBalanceCheckLocked(userId, pkgName);
+            }
+        }
+    }
+
+    static final class ActionAffordabilityNote {
+        private final EconomyManagerInternal.ActionBill mActionBill;
+        private final EconomyManagerInternal.AffordabilityChangeListener mListener;
+        private long mModifiedPrice;
+        private boolean mIsAffordable;
+
+        @VisibleForTesting
+        ActionAffordabilityNote(@NonNull EconomyManagerInternal.ActionBill bill,
+                @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+                @NonNull EconomicPolicy economicPolicy) {
+            mActionBill = bill;
+            final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
+                    bill.getAnticipatedActions();
+            for (int i = 0; i < anticipatedActions.size(); ++i) {
+                final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
+                final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId);
+                if (action == null) {
+                    throw new IllegalArgumentException("Invalid action id: " + aa.actionId);
+                }
+            }
+            mListener = listener;
+        }
+
+        @NonNull
+        EconomyManagerInternal.ActionBill getActionBill() {
+            return mActionBill;
+        }
+
+        @NonNull
+        EconomyManagerInternal.AffordabilityChangeListener getListener() {
+            return mListener;
+        }
+
+        private long getCachedModifiedPrice() {
+            return mModifiedPrice;
+        }
+
+        @VisibleForTesting
+        long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
+                int userId, @NonNull String pkgName) {
+            long modifiedPrice = 0;
+            final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
+                    mActionBill.getAnticipatedActions();
+            for (int i = 0; i < anticipatedActions.size(); ++i) {
+                final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
+
+                final long actionCost =
+                        economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
+                modifiedPrice += actionCost * aa.numInstantaneousCalls
+                        + actionCost * (aa.ongoingDurationMs / 1000);
+            }
+            mModifiedPrice = modifiedPrice;
+            return modifiedPrice;
+        }
+
+        boolean isCurrentlyAffordable() {
+            return mIsAffordable;
+        }
+
+        private void setNewAffordability(boolean isAffordable) {
+            mIsAffordable = isAffordable;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ActionAffordabilityNote)) return false;
+            ActionAffordabilityNote other = (ActionAffordabilityNote) o;
+            return mActionBill.equals(other.mActionBill)
+                    && mListener.equals(other.mListener);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 0;
+            hash = 31 * hash + Objects.hash(mListener);
+            hash = 31 * hash + mActionBill.hashCode();
+            return hash;
+        }
+    }
+
+    private final class AgentHandler extends Handler {
+        AgentHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CHECK_BALANCE: {
+                    final int userId = msg.arg1;
+                    final String pkgName = (String) msg.obj;
+                    synchronized (mLock) {
+                        final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                                mActionAffordabilityNotes.get(userId, pkgName);
+                        if (actionAffordabilityNotes != null
+                                && actionAffordabilityNotes.size() > 0) {
+                            final long newBalance = getBalanceLocked(userId, pkgName);
+
+                            for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+                                final ActionAffordabilityNote note =
+                                        actionAffordabilityNotes.valueAt(i);
+                                final boolean isAffordable =
+                                        newBalance >= note.getCachedModifiedPrice();
+                                if (note.isCurrentlyAffordable() != isAffordable) {
+                                    note.setNewAffordability(isAffordable);
+                                    mIrs.postAffordabilityChanged(userId, pkgName, note);
+                                }
+                            }
+                        }
+                        scheduleBalanceCheckLocked(userId, pkgName);
+                    }
+                }
+                break;
+
+                case MSG_CLEAN_LEDGER: {
+                    final int userId = msg.arg1;
+                    final String pkgName = (String) msg.obj;
+                    synchronized (mLock) {
+                        final Ledger ledger = getLedgerLocked(userId, pkgName);
+                        ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
+                    }
+                }
+                break;
+
+                case MSG_SET_ALARMS: {
+                    synchronized (mLock) {
+                        mLedgerCleanupAlarmListener.setNextAlarmLocked();
+                        mBalanceThresholdAlarmListener.setNextAlarmLocked();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    void dumpLocked(IndentingPrintWriter pw) {
+        pw.print("Current GDP: ");
+        pw.println(narcToString(mCurrentNarcsInCirculation));
+
+        pw.println();
+        mBalanceThresholdAlarmListener.dumpLocked(pw);
+
+        pw.println();
+        mLedgerCleanupAlarmListener.dumpLocked(pw);
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
new file mode 100644
index 0000000..acdf689
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * AlarmManager.
+ */
+public class AlarmManagerEconomicPolicy extends EconomicPolicy {
+    public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
+            TYPE_ACTION | POLICY_AM | 0;
+    public static final int ACTION_ALARM_WAKEUP_EXACT =
+            TYPE_ACTION | POLICY_AM | 1;
+    public static final int ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+            TYPE_ACTION | POLICY_AM | 2;
+    public static final int ACTION_ALARM_WAKEUP_INEXACT =
+            TYPE_ACTION | POLICY_AM | 3;
+    public static final int ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE =
+            TYPE_ACTION | POLICY_AM | 4;
+    public static final int ACTION_ALARM_NONWAKEUP_EXACT =
+            TYPE_ACTION | POLICY_AM | 5;
+    public static final int ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+            TYPE_ACTION | POLICY_AM | 6;
+    public static final int ACTION_ALARM_NONWAKEUP_INEXACT =
+            TYPE_ACTION | POLICY_AM | 7;
+    public static final int ACTION_ALARM_CLOCK =
+            TYPE_ACTION | POLICY_AM | 8;
+
+    private static final int[] COST_MODIFIERS = new int[]{
+            COST_MODIFIER_CHARGING,
+            COST_MODIFIER_DEVICE_IDLE,
+            COST_MODIFIER_POWER_SAVE_MODE,
+            COST_MODIFIER_PROCESS_STATE
+    };
+
+    private final SparseArray<Action> mActions = new SparseArray<>();
+    private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+    AlarmManagerEconomicPolicy(InternalResourceService irs) {
+        super(irs);
+        loadActions();
+        loadRewards();
+    }
+
+    @Override
+    long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+        // TODO: take exemption into account
+        return arcToNarc(160);
+    }
+
+    @Override
+    long getMaxSatiatedBalance() {
+        return arcToNarc(1440);
+    }
+
+
+    @Override
+    long getMaxSatiatedCirculation() {
+        return arcToNarc(52000);
+    }
+
+    @NonNull
+    @Override
+    int[] getCostModifiers() {
+        return COST_MODIFIERS;
+    }
+
+    @Nullable
+    @Override
+    Action getAction(@AppAction int actionId) {
+        return mActions.get(actionId);
+    }
+
+    @Nullable
+    @Override
+    Reward getReward(@UtilityReward int rewardId) {
+        return mRewards.get(rewardId);
+    }
+
+    private void loadActions() {
+        mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+                new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, arcToNarc(3), arcToNarc(5)));
+        mActions.put(ACTION_ALARM_WAKEUP_EXACT,
+                new Action(ACTION_ALARM_WAKEUP_EXACT, arcToNarc(3), arcToNarc(4)));
+        mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+                new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+                        arcToNarc(3), arcToNarc(4)));
+        mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
+                new Action(ACTION_ALARM_WAKEUP_INEXACT, arcToNarc(3), arcToNarc(3)));
+        mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+                new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+                        arcToNarc(1), arcToNarc(3)));
+        mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
+                new Action(ACTION_ALARM_NONWAKEUP_EXACT, arcToNarc(1), arcToNarc(2)));
+        mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+                new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+                        arcToNarc(1), arcToNarc(2)));
+        mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
+                new Action(ACTION_ALARM_NONWAKEUP_INEXACT, arcToNarc(1), arcToNarc(1)));
+        mActions.put(ACTION_ALARM_CLOCK,
+                new Action(ACTION_ALARM_CLOCK, arcToNarc(5), arcToNarc(10)));
+    }
+
+    private void loadRewards() {
+        mRewards.put(REWARD_TOP_ACTIVITY,
+                new Reward(REWARD_TOP_ACTIVITY,
+                        arcToNarc(0), /* .01 arcs */ arcToNarc(1) / 100, arcToNarc(500)));
+        mRewards.put(REWARD_NOTIFICATION_SEEN,
+                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(3), arcToNarc(0), arcToNarc(60)));
+        mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+                new Reward(REWARD_NOTIFICATION_INTERACTION,
+                        arcToNarc(5), arcToNarc(0), arcToNarc(500)));
+        mRewards.put(REWARD_WIDGET_INTERACTION,
+                new Reward(REWARD_WIDGET_INTERACTION, arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+        mRewards.put(REWARD_OTHER_USER_INTERACTION,
+                new Reward(REWARD_OTHER_USER_INTERACTION,
+                        arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
new file mode 100644
index 0000000..a627de6
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+/** Modifier that makes things free when the device is charging. */
+class ChargingModifier extends Modifier {
+    private static final String TAG = "TARE-" + ChargingModifier.class.getSimpleName();
+    private static final boolean DEBUG = InternalResourceService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    private final InternalResourceService mIrs;
+    private final ChargingTracker mChargingTracker;
+
+    ChargingModifier(@NonNull InternalResourceService irs) {
+        super();
+        mIrs = irs;
+        mChargingTracker = new ChargingTracker();
+        mChargingTracker.startTracking(irs.getContext());
+    }
+
+    @Override
+    long getModifiedCostToProduce(long ctp) {
+        return modifyValue(ctp);
+    }
+
+    @Override
+    long getModifiedPrice(long price) {
+        return modifyValue(price);
+    }
+
+    private long modifyValue(long val) {
+        if (mChargingTracker.mCharging) {
+            return 0;
+        }
+        return val;
+    }
+
+    @Override
+    void dump(IndentingPrintWriter pw) {
+        pw.print("charging=");
+        pw.println(mChargingTracker.mCharging);
+    }
+
+    private final class ChargingTracker extends BroadcastReceiver {
+        /**
+         * Track whether we're "charging", where charging means that we're ready to commit to
+         * doing work.
+         */
+        private volatile boolean mCharging;
+
+        public void startTracking(@NonNull Context context) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(BatteryManager.ACTION_CHARGING);
+            filter.addAction(BatteryManager.ACTION_DISCHARGING);
+            context.registerReceiver(this, filter);
+
+            // Initialise tracker state.
+            final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+            mCharging = batteryManager.isCharging();
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (BatteryManager.ACTION_CHARGING.equals(action)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Received charging intent, fired @ "
+                            + SystemClock.elapsedRealtime());
+                }
+                if (!mCharging) {
+                    mCharging = true;
+                    mIrs.onDeviceStateChanged();
+                }
+            } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Disconnected from power.");
+                }
+                if (mCharging) {
+                    mCharging = false;
+                    mIrs.onDeviceStateChanged();
+                }
+            }
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
new file mode 100644
index 0000000..8c56c61
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+
+/** Combines all enabled policies into one. */
+public class CompleteEconomicPolicy extends EconomicPolicy {
+    private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
+    /** Lazily populated set of actions covered by this policy. */
+    private final SparseArray<Action> mActions = new SparseArray<>();
+    /** Lazily populated set of rewards covered by this policy. */
+    private final SparseArray<Reward> mRewards = new SparseArray<>();
+    private final int[] mCostModifiers;
+    private final long mMaxSatiatedBalance;
+    private final long mMaxSatiatedCirculation;
+
+    CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
+        super(irs);
+        mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
+        mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));
+
+        ArraySet<Integer> costModifiers = new ArraySet<>();
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
+            for (int s : sm) {
+                costModifiers.add(s);
+            }
+        }
+        mCostModifiers = new int[costModifiers.size()];
+        for (int i = 0; i < costModifiers.size(); ++i) {
+            mCostModifiers[i] = costModifiers.valueAt(i);
+        }
+
+        long max = 0;
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
+        }
+        mMaxSatiatedBalance = max;
+
+        max = 0;
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+        }
+        mMaxSatiatedCirculation = max;
+    }
+
+    @Override
+    public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+        long min = 0;
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
+        }
+        return min;
+    }
+
+    @Override
+    public long getMaxSatiatedBalance() {
+        return mMaxSatiatedBalance;
+    }
+
+    @Override
+    public long getMaxSatiatedCirculation() {
+        return mMaxSatiatedCirculation;
+    }
+
+    @NonNull
+    @Override
+    public int[] getCostModifiers() {
+        return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
+    }
+
+    @Nullable
+    @Override
+    public Action getAction(@AppAction int actionId) {
+        if (mActions.contains(actionId)) {
+            return mActions.get(actionId);
+        }
+
+        long ctp = 0, price = 0;
+        boolean exists = false;
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId);
+            if (a != null) {
+                exists = true;
+                ctp += a.costToProduce;
+                price += a.basePrice;
+            }
+        }
+        final Action action = exists ? new Action(actionId, ctp, price) : null;
+        mActions.put(actionId, action);
+        return action;
+    }
+
+    @Nullable
+    @Override
+    public Reward getReward(@UtilityReward int rewardId) {
+        if (mRewards.contains(rewardId)) {
+            return mRewards.get(rewardId);
+        }
+
+        long instantReward = 0, ongoingReward = 0, maxReward = 0;
+        boolean exists = false;
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId);
+            if (r != null) {
+                exists = true;
+                instantReward += r.instantReward;
+                ongoingReward += r.ongoingRewardPerSecond;
+                maxReward += r.maxDailyReward;
+            }
+        }
+        final Reward reward = exists
+                ? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null;
+        mRewards.put(rewardId, reward);
+        return reward;
+    }
+
+    @Override
+    void dump(IndentingPrintWriter pw) {
+        dumpActiveModifiers(pw);
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
new file mode 100644
index 0000000..20b9c70
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+/** Modifier that makes things more expensive in light and deep doze. */
+class DeviceIdleModifier extends Modifier {
+    private static final String TAG = "TARE-" + DeviceIdleModifier.class.getSimpleName();
+    private static final boolean DEBUG = InternalResourceService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    private final InternalResourceService mIrs;
+    private final PowerManager mPowerManager;
+    private final DeviceIdleTracker mDeviceIdleTracker;
+
+    DeviceIdleModifier(@NonNull InternalResourceService irs) {
+        super();
+        mIrs = irs;
+        mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+        mDeviceIdleTracker = new DeviceIdleTracker();
+        mDeviceIdleTracker.startTracking(irs.getContext());
+    }
+
+    @Override
+    long getModifiedCostToProduce(long ctp) {
+        if (mDeviceIdleTracker.mDeviceIdle) {
+            return (long) (1.2 * ctp);
+        }
+        if (mDeviceIdleTracker.mDeviceLightIdle) {
+            return (long) (1.1 * ctp);
+        }
+        return ctp;
+    }
+
+    @Override
+    void dump(IndentingPrintWriter pw) {
+        pw.print("idle=");
+        pw.println(mDeviceIdleTracker.mDeviceIdle);
+        pw.print("lightIdle=");
+        pw.println(mDeviceIdleTracker.mDeviceLightIdle);
+    }
+
+    private final class DeviceIdleTracker extends BroadcastReceiver {
+
+        private volatile boolean mDeviceIdle;
+        private volatile boolean mDeviceLightIdle;
+
+        DeviceIdleTracker() {
+        }
+
+        void startTracking(@NonNull Context context) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+            filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+            context.registerReceiver(this, filter);
+
+            // Initialise tracker state.
+            mDeviceIdle = mPowerManager.isDeviceIdleMode();
+            mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+                if (mDeviceIdle != mPowerManager.isDeviceIdleMode()) {
+                    mDeviceIdle = mPowerManager.isDeviceIdleMode();
+                    mIrs.onDeviceStateChanged();
+                }
+            } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+                if (mDeviceIdle != mPowerManager.isLightDeviceIdleMode()) {
+                    mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+                    mIrs.onDeviceStateChanged();
+                }
+            }
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
new file mode 100644
index 0000000..f08dd24
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions.
+ * Policies are defined per participating system service. This allows each service’s EconomicPolicy
+ * to be isolated while allowing the core economic system to scale across policies to achieve a
+ * logical system-wide value system.
+ */
+public abstract class EconomicPolicy {
+    private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName();
+
+    private static final int SHIFT_TYPE = 30;
+    static final int MASK_TYPE = 0b11 << SHIFT_TYPE;
+    static final int TYPE_REGULATION = 0 << SHIFT_TYPE;
+    static final int TYPE_ACTION = 1 << SHIFT_TYPE;
+    static final int TYPE_REWARD = 2 << SHIFT_TYPE;
+
+    private static final int SHIFT_POLICY = 29;
+    static final int MASK_POLICY = 0b1 << SHIFT_POLICY;
+    static final int POLICY_AM = 0 << SHIFT_POLICY;
+    static final int POLICY_JS = 1 << SHIFT_POLICY;
+
+    static final int MASK_EVENT = ~0 - (0b111 << SHIFT_POLICY);
+
+    static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0;
+    static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1;
+    static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2;
+
+    static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0;
+    static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1;
+    static final int REWARD_TOP_ACTIVITY = TYPE_REWARD | 2;
+    static final int REWARD_WIDGET_INTERACTION = TYPE_REWARD | 3;
+    static final int REWARD_OTHER_USER_INTERACTION = TYPE_REWARD | 4;
+
+    @IntDef({
+            AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT,
+            AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK,
+            JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START,
+            JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING,
+            JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START,
+            JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING,
+            JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START,
+            JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING,
+            JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START,
+            JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING,
+            JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START,
+            JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING,
+            JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AppAction {
+    }
+
+    @IntDef({
+            TYPE_ACTION,
+            TYPE_REGULATION,
+            TYPE_REWARD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {
+    }
+
+    @IntDef({
+            REWARD_TOP_ACTIVITY,
+            REWARD_NOTIFICATION_SEEN,
+            REWARD_NOTIFICATION_INTERACTION,
+            REWARD_WIDGET_INTERACTION,
+            REWARD_OTHER_USER_INTERACTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UtilityReward {
+    }
+
+    static class Action {
+        /** Unique id (including across policies) for this action. */
+        public final int id;
+        /**
+         * How many ARCs the system says it takes to perform this action.
+         */
+        public final long costToProduce;
+        /**
+         * The base price to perform this action. If this is
+         * less than the {@link #costToProduce}, then the system should not perform
+         * the action unless a modifier lowers the cost to produce.
+         */
+        public final long basePrice;
+
+        Action(int id, long costToProduce, long basePrice) {
+            this.id = id;
+            this.costToProduce = costToProduce;
+            this.basePrice = basePrice;
+        }
+    }
+
+    static class Reward {
+        /** Unique id (including across policies) for this reward. */
+        @UtilityReward
+        public final int id;
+        public final long instantReward;
+        /** Reward credited per second of ongoing activity. */
+        public final long ongoingRewardPerSecond;
+        /** The maximum amount an app can earn from this reward within a 24 hour period. */
+        public final long maxDailyReward;
+
+        Reward(int id, long instantReward, long ongoingReward, long maxDailyReward) {
+            this.id = id;
+            this.instantReward = instantReward;
+            this.ongoingRewardPerSecond = ongoingReward;
+            this.maxDailyReward = maxDailyReward;
+        }
+    }
+
+    private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
+
+    EconomicPolicy(@NonNull InternalResourceService irs) {
+        for (int mId : getCostModifiers()) {
+            initModifier(mId, irs);
+        }
+    }
+
+    @CallSuper
+    void onSystemServicesReady() {
+        for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+            final Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+            if (modifier != null) {
+                modifier.onSystemServicesReady();
+            }
+        }
+    }
+
+    /**
+     * Returns the minimum suggested balance an app should have when the device is at 100% battery.
+     * This takes into account any exemptions the app may have.
+     */
+    abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName);
+
+    /**
+     * Returns the maximum balance an app should have when the device is at 100% battery. This
+     * exists to ensure that no single app accumulate all available resources and increases fairness
+     * for all apps.
+     */
+    abstract long getMaxSatiatedBalance();
+
+    /**
+     * Returns the maximum number of narcs that should be in circulation at once when the device is
+     * at 100% battery.
+     */
+    abstract long getMaxSatiatedCirculation();
+
+    /** Return the set of modifiers that should apply to this policy's costs. */
+    @NonNull
+    abstract int[] getCostModifiers();
+
+    @Nullable
+    abstract Action getAction(@AppAction int actionId);
+
+    @Nullable
+    abstract Reward getReward(@UtilityReward int rewardId);
+
+    void dump(IndentingPrintWriter pw) {
+    }
+
+    final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+        final Action action = getAction(actionId);
+        if (action == null) {
+            return 0;
+        }
+        long ctp = action.costToProduce;
+        long price = action.basePrice;
+        final int[] costModifiers = getCostModifiers();
+        boolean useProcessStatePriceDeterminant = false;
+        for (int costModifier : costModifiers) {
+            if (costModifier == COST_MODIFIER_PROCESS_STATE) {
+                useProcessStatePriceDeterminant = true;
+            } else {
+                final Modifier modifier = getModifier(costModifier);
+                ctp = modifier.getModifiedCostToProduce(ctp);
+                price = modifier.getModifiedPrice(price);
+            }
+        }
+        // ProcessStateModifier needs to be done last.
+        if (useProcessStatePriceDeterminant) {
+            ProcessStateModifier processStateModifier =
+                    (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
+            price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
+        }
+        return price;
+    }
+
+    private static void initModifier(@Modifier.CostModifier final int modifierId,
+            @NonNull InternalResourceService irs) {
+        if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+            throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+        }
+        Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+        if (modifier == null) {
+            switch (modifierId) {
+                case COST_MODIFIER_CHARGING:
+                    modifier = new ChargingModifier(irs);
+                    break;
+                case COST_MODIFIER_DEVICE_IDLE:
+                    modifier = new DeviceIdleModifier(irs);
+                    break;
+                case COST_MODIFIER_POWER_SAVE_MODE:
+                    modifier = new PowerSaveModeModifier(irs);
+                    break;
+                case COST_MODIFIER_PROCESS_STATE:
+                    modifier = new ProcessStateModifier(irs);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+            }
+            COST_MODIFIER_BY_INDEX[modifierId] = modifier;
+        }
+    }
+
+    @NonNull
+    private static Modifier getModifier(@Modifier.CostModifier final int modifierId) {
+        if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+            throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+        }
+        final Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+        if (modifier == null) {
+            throw new IllegalStateException(
+                    "Modifier #" + modifierId + " was never initialized");
+        }
+        return modifier;
+    }
+
+    @EventType
+    static int getEventType(int eventId) {
+        return eventId & MASK_TYPE;
+    }
+
+    @NonNull
+    static String eventToString(int eventId) {
+        switch (eventId & MASK_TYPE) {
+            case TYPE_ACTION:
+                return actionToString(eventId);
+
+            case TYPE_REGULATION:
+                return regulationToString(eventId);
+
+            case TYPE_REWARD:
+                return rewardToString(eventId);
+
+            default:
+                return "UNKNOWN_EVENT:" + Integer.toHexString(eventId);
+        }
+    }
+
+    @NonNull
+    static String actionToString(int eventId) {
+        switch (eventId & MASK_POLICY) {
+            case POLICY_AM:
+                switch (eventId) {
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE:
+                        return "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT:
+                        return "ALARM_WAKEUP_EXACT";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+                        return "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT:
+                        return "ALARM_WAKEUP_INEXACT";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE:
+                        return "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT:
+                        return "ALARM_NONWAKEUP_EXACT";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+                        return "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT:
+                        return "ALARM_NONWAKEUP_INEXACT";
+                    case AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK:
+                        return "ALARM_CLOCK";
+                }
+                break;
+
+            case POLICY_JS:
+                switch (eventId) {
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START:
+                        return "JOB_MAX_START";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING:
+                        return "JOB_MAX_RUNNING";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START:
+                        return "JOB_HIGH_START";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING:
+                        return "JOB_HIGH_RUNNING";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START:
+                        return "JOB_DEFAULT_START";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING:
+                        return "JOB_DEFAULT_RUNNING";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START:
+                        return "JOB_LOW_START";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING:
+                        return "JOB_LOW_RUNNING";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START:
+                        return "JOB_MIN_START";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING:
+                        return "JOB_MIN_RUNNING";
+                    case JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT:
+                        return "JOB_TIMEOUT";
+                }
+                break;
+        }
+        return "UNKNOWN_ACTION:" + Integer.toHexString(eventId);
+    }
+
+    @NonNull
+    static String regulationToString(int eventId) {
+        switch (eventId) {
+            case REGULATION_BASIC_INCOME:
+                return "BASIC_INCOME";
+            case REGULATION_BIRTHRIGHT:
+                return "BIRTHRIGHT";
+            case REGULATION_WEALTH_RECLAMATION:
+                return "WEALTH_RECLAMATION";
+        }
+        return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId);
+    }
+
+    @NonNull
+    static String rewardToString(int eventId) {
+        switch (eventId) {
+            case REWARD_TOP_ACTIVITY:
+                return "REWARD_TOP_ACTIVITY";
+            case REWARD_NOTIFICATION_SEEN:
+                return "REWARD_NOTIFICATION_SEEN";
+            case REWARD_NOTIFICATION_INTERACTION:
+                return "REWARD_NOTIFICATION_INTERACTION";
+            case REWARD_WIDGET_INTERACTION:
+                return "REWARD_WIDGET_INTERACTION";
+            case REWARD_OTHER_USER_INTERACTION:
+                return "REWARD_OTHER_USER_INTERACTION";
+        }
+        return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
+    }
+
+    protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
+        for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+            pw.print("Modifier ");
+            pw.println(i);
+            pw.increaseIndent();
+
+            Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+            if (modifier != null) {
+                modifier.dump(pw);
+            } else {
+                pw.println("NOT ACTIVE");
+            }
+
+            pw.decreaseIndent();
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
new file mode 100644
index 0000000..831c05f
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Interface for the system server to deal with the resource economy subsystem.
+ *
+ * @hide
+ */
+public interface EconomyManagerInternal {
+    /**
+     * Used to indicate a future action an app is expected to take.
+     */
+    final class AnticipatedAction {
+        public final int actionId;
+        public final int numInstantaneousCalls;
+        public final long ongoingDurationMs;
+        private final int mHashCode;
+
+        /**
+         * @param actionId              The expected action
+         * @param numInstantaneousCalls How many instantaneous times the action will be performed
+         * @param ongoingDurationMs     An estimate of how long the ongoing event will go on for
+         */
+        public AnticipatedAction(@EconomicPolicy.AppAction int actionId,
+                int numInstantaneousCalls, long ongoingDurationMs) {
+            this.actionId = actionId;
+            this.numInstantaneousCalls = numInstantaneousCalls;
+            this.ongoingDurationMs = ongoingDurationMs;
+
+            int hash = 0;
+            hash = 31 * hash + actionId;
+            hash = 31 * hash + numInstantaneousCalls;
+            hash = 31 * hash + (int) (ongoingDurationMs ^ (ongoingDurationMs >>> 32));
+            mHashCode = hash;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            AnticipatedAction that = (AnticipatedAction) o;
+            return actionId == that.actionId
+                    && numInstantaneousCalls == that.numInstantaneousCalls
+                    && ongoingDurationMs == that.ongoingDurationMs;
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+    }
+
+    /**
+     * A collection of {@link AnticipatedAction}s that will be performed together.
+     */
+    final class ActionBill {
+        private static final Comparator<AnticipatedAction>
+                sAnticipatedActionComparator = Comparator.comparingInt(aa -> aa.actionId);
+
+        private final List<AnticipatedAction> mAnticipatedActions;
+        private final int mHashCode;
+
+        public ActionBill(@NonNull List<AnticipatedAction> anticipatedActions) {
+            List<AnticipatedAction> actions = new ArrayList<>(anticipatedActions);
+            actions.sort(sAnticipatedActionComparator);
+            mAnticipatedActions = Collections.unmodifiableList(actions);
+
+            int hash = 0;
+            for (int i = 0; i < mAnticipatedActions.size(); ++i) {
+                hash = 31 * hash + mAnticipatedActions.get(i).hashCode();
+            }
+            mHashCode = hash;
+        }
+
+        List<AnticipatedAction> getAnticipatedActions() {
+            return mAnticipatedActions;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ActionBill that = (ActionBill) o;
+            return mAnticipatedActions.equals(that.mAnticipatedActions);
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+    }
+
+    /** Listener for when an app's ability to afford a bill changes. */
+    interface AffordabilityChangeListener {
+        void onAffordabilityChanged(int userId, @NonNull String pkgName, @NonNull ActionBill bill,
+                boolean canAfford);
+    }
+
+    /**
+     * Return {@code true} if the app is able to pay for the anticipated actions.
+     */
+    boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
+
+
+    /**
+     * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
+     * indicated bill changes.
+     */
+    void registerAffordabilityChangeListener(int userId, @NonNull String pkgName,
+            @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
+
+    /**
+     * Unregister a {@link AffordabilityChangeListener} from being notified of any changes to an
+     * app's ability to afford the specified bill.
+     */
+    void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName,
+            @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
+
+    /**
+     * Note that an instantaneous event has occurred. The event must be specified in one of the
+     * EconomicPolicies.
+     *
+     * @param tag An optional tag that can be used to differentiate the same event for the same app.
+     */
+    void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+            @Nullable String tag);
+
+    /**
+     * Note that a long-running event is starting. The event must be specified in one of the
+     * EconomicPolicies. You must always call
+     * {@link #noteOngoingEventStopped(int, String, int, String)} to end the event. Ongoing
+     * events will be separated and grouped by event-tag combinations. There must be an equal
+     * number of start() and stop() calls for the same event-tag combination in order for the
+     * tracking to finally stop (ie. ongoing events are ref-counted).
+     *
+     * @param tag An optional tag that can be used to differentiate the same event for the same app.
+     */
+    void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+            @Nullable String tag);
+
+    /**
+     * Note that a long-running event has stopped. The event must be specified in one of the
+     * EconomicPolicies. Ongoing events are separated and grouped by event-tag combinations.
+     * There must be an equal number of start() and stop() calls for the same event-tag combination
+     * in order for the tracking to finally stop (ie. ongoing events are ref-counted).
+     *
+     * @param tag An optional tag that can be used to differentiate the same event for the same app.
+     */
+    void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+            @Nullable String tag);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
new file mode 100644
index 0000000..cfc6645
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.BatteryManagerInternal;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseSetArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
+ * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details
+ * while the {@link Agent} deals with the nitty-gritty details.
+ *
+ * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}.
+ *
+ * @hide
+ */
+public class InternalResourceService extends SystemService {
+    public static final String TAG = "TARE-IRS";
+    public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);
+
+    static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
+    /** How much of an app's unused wealth should be reclaimed periodically. */
+    private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
+
+    /** Global local for all resource economy state. */
+    private final Object mLock = new Object();
+
+    private final Handler mHandler;
+    private final BatteryManagerInternal mBatteryManagerInternal;
+    private final PackageManager mPackageManager;
+
+    private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+    private final Agent mAgent;
+
+    @NonNull
+    @GuardedBy("mLock")
+    private List<PackageInfo> mPkgCache = new ArrayList<>();
+
+    /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
+    @GuardedBy("mLock")
+    private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+
+    @GuardedBy("mLock")
+    private boolean mIsSetup;
+    // In the range [0,100] to represent 0% to 100% battery.
+    @GuardedBy("mLock")
+    private int mCurrentBatteryLevel;
+    // TODO: load from disk
+    @GuardedBy("mLock")
+    private long mLastUnusedReclamationTime;
+
+    @SuppressWarnings("FieldCanBeLocal")
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Nullable
+        private String getPackageName(Intent intent) {
+            Uri uri = intent.getData();
+            return uri != null ? uri.getSchemeSpecificPart() : null;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_BATTERY_LEVEL_CHANGED:
+                    onBatteryLevelChanged();
+                    break;
+                case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
+                    final String pkgName = getPackageName(intent);
+                    final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                    onPackageRemoved(pkgUid, pkgName);
+                }
+                break;
+                case Intent.ACTION_PACKAGE_ADDED: {
+                    if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                        final String pkgName = getPackageName(intent);
+                        final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                        onPackageAdded(pkgUid, pkgName);
+                    }
+                }
+                break;
+                case Intent.ACTION_PACKAGE_RESTARTED: {
+                    final String pkgName = getPackageName(intent);
+                    final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                    final int userId = UserHandle.getUserId(pkgUid);
+                    onPackageForceStopped(userId, pkgName);
+                }
+                break;
+                case Intent.ACTION_USER_ADDED: {
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    onUserAdded(userId);
+                }
+                break;
+                case Intent.ACTION_USER_REMOVED: {
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    onUserRemoved(userId);
+                }
+                break;
+            }
+        }
+    };
+
+    private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener =
+            new AlarmManager.OnAlarmListener() {
+                @Override
+                public void onAlarm() {
+                    synchronized (mLock) {
+                        mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
+                        mLastUnusedReclamationTime = System.currentTimeMillis();
+                        scheduleUnusedWealthReclamationLocked();
+                    }
+                }
+            };
+
+    private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
+    private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
+    private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
+    private static final String KEY_PKG = "pkg";
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public InternalResourceService(Context context) {
+        super(context);
+
+        mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
+        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+        mPackageManager = context.getPackageManager();
+        mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
+        mAgent = new Agent(this, mCompleteEconomicPolicy);
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+        final IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        pkgFilter.addDataScheme("package");
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
+        final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+        userFilter.addAction(Intent.ACTION_USER_ADDED);
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+        publishLocalService(EconomyManagerInternal.class, new LocalService());
+    }
+
+    @Override
+    public void onStart() {
+
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (PHASE_SYSTEM_SERVICES_READY == phase) {
+            synchronized (mLock) {
+                mCurrentBatteryLevel = getCurrentBatteryLevel();
+                // TODO: base on if we have anything persisted
+                final boolean isFirstSetup = true;
+                if (isFirstSetup) {
+                    mHandler.post(this::setupEconomy);
+                } else {
+                    mIsSetup = true;
+                }
+                scheduleUnusedWealthReclamationLocked();
+                mCompleteEconomicPolicy.onSystemServicesReady();
+            }
+        }
+    }
+
+    @NonNull
+    Object getLock() {
+        return mLock;
+    }
+
+    @NonNull
+    List<PackageInfo> getInstalledPackages() {
+        synchronized (mLock) {
+            return mPkgCache;
+        }
+    }
+
+    @GuardedBy("mLock")
+    long getMaxCirculationLocked() {
+        return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+    }
+
+    @GuardedBy("mLock")
+    long getMinBalanceLocked(final int userId, @NonNull final String pkgName) {
+        return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName)
+                / 100;
+    }
+
+    void onBatteryLevelChanged() {
+        synchronized (mLock) {
+            final int newBatteryLevel = getCurrentBatteryLevel();
+            if (newBatteryLevel > mCurrentBatteryLevel) {
+                mAgent.distributeBasicIncomeLocked(newBatteryLevel);
+            }
+            mCurrentBatteryLevel = newBatteryLevel;
+        }
+    }
+
+    void onDeviceStateChanged() {
+        synchronized (mLock) {
+            mAgent.onDeviceStateChangedLocked();
+        }
+    }
+
+    void onPackageAdded(final int uid, @NonNull final String pkgName) {
+        final int userId = UserHandle.getUserId(uid);
+        final PackageInfo packageInfo;
+        try {
+            packageInfo = mPackageManager.getPackageInfoAsUser(pkgName, 0, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName);
+            return;
+        }
+        synchronized (mLock) {
+            mPkgCache.add(packageInfo);
+            mUidToPackageCache.add(uid, pkgName);
+            // TODO: only do this when the user first launches the app (app leaves stopped state)
+            mAgent.grantBirthrightLocked(userId, pkgName);
+        }
+    }
+
+    void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
+        synchronized (mLock) {
+            // TODO: reduce ARC count by some amount
+        }
+    }
+
+    void onPackageRemoved(final int uid, @NonNull final String pkgName) {
+        final int userId = UserHandle.getUserId(uid);
+        synchronized (mLock) {
+            mUidToPackageCache.remove(uid, pkgName);
+            for (int i = 0; i < mPkgCache.size(); ++i) {
+                PackageInfo pkgInfo = mPkgCache.get(i);
+                if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
+                        && pkgName.equals(pkgInfo.packageName)) {
+                    mPkgCache.remove(i);
+                    break;
+                }
+            }
+            mAgent.onPackageRemovedLocked(userId, pkgName);
+        }
+    }
+
+    void onUidStateChanged(final int uid) {
+        synchronized (mLock) {
+            final ArraySet<String> pkgNames = getPackagesForUidLocked(uid);
+            if (pkgNames == null) {
+                Slog.e(TAG, "Don't have packages for uid " + uid);
+            } else {
+                mAgent.onAppStatesChangedLocked(UserHandle.getUserId(uid), pkgNames);
+            }
+        }
+    }
+
+    void onUserAdded(final int userId) {
+        synchronized (mLock) {
+            loadInstalledPackageListLocked();
+            mAgent.grantBirthrightsLocked(userId);
+        }
+    }
+
+    void onUserRemoved(final int userId) {
+        synchronized (mLock) {
+            ArrayList<String> removedPkgs = new ArrayList<>();
+            for (int i = mPkgCache.size() - 1; i >= 0; --i) {
+                PackageInfo pkgInfo = mPkgCache.get(i);
+                if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
+                    removedPkgs.add(pkgInfo.packageName);
+                    mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
+                    mPkgCache.remove(i);
+                    break;
+                }
+            }
+            loadInstalledPackageListLocked();
+            mAgent.onUserRemovedLocked(userId, removedPkgs);
+        }
+    }
+
+    void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
+            @NonNull Agent.ActionAffordabilityNote affordabilityNote) {
+        if (DEBUG) {
+            Slog.d(TAG, userId + ":" + pkgName + " affordability changed to "
+                    + affordabilityNote.isCurrentlyAffordable());
+        }
+        Message msg = mHandler.obtainMessage(
+                MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, userId, 0, affordabilityNote);
+        Bundle data = new Bundle();
+        data.putString(KEY_PKG, pkgName);
+        msg.setData(data);
+        msg.sendToTarget();
+    }
+
+    @GuardedBy("mLock")
+    private void scheduleUnusedWealthReclamationLocked() {
+        final long now = System.currentTimeMillis();
+        final long nextReclamationTime =
+                Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000);
+        mHandler.post(() -> {
+            // Never call out to AlarmManager with the lock held. This sits below AM.
+            AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
+            if (alarmManager != null) {
+                alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+                        SystemClock.elapsedRealtime() + (nextReclamationTime - now),
+                        30 * MINUTE_IN_MILLIS,
+                        ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler);
+            } else {
+                mHandler.sendEmptyMessageDelayed(
+                        MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, 30_000);
+            }
+        });
+    }
+
+    private int getCurrentBatteryLevel() {
+        return mBatteryManagerInternal.getBatteryLevel();
+    }
+
+    @Nullable
+    @GuardedBy("mLock")
+    private ArraySet<String> getPackagesForUidLocked(final int uid) {
+        ArraySet<String> packages = mUidToPackageCache.get(uid);
+        if (packages == null) {
+            final String[] pkgs = mPackageManager.getPackagesForUid(uid);
+            if (pkgs != null) {
+                for (String pkg : pkgs) {
+                    mUidToPackageCache.add(uid, pkg);
+                }
+                packages = mUidToPackageCache.get(uid);
+            }
+        }
+        return packages;
+    }
+
+    @GuardedBy("mLock")
+    private void loadInstalledPackageListLocked() {
+        mPkgCache = mPackageManager.getInstalledPackages(0);
+    }
+
+    private void setupEconomy() {
+        synchronized (mLock) {
+            loadInstalledPackageListLocked();
+            mAgent.grantBirthrightsLocked();
+            mIsSetup = true;
+        }
+    }
+
+    private final class IrsHandler extends Handler {
+        IrsHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
+                    Bundle data = msg.getData();
+                    final int userId = msg.arg1;
+                    final String pkgName = data.getString(KEY_PKG);
+                    final Agent.ActionAffordabilityNote affordabilityNote =
+                            (Agent.ActionAffordabilityNote) msg.obj;
+                    final EconomyManagerInternal.AffordabilityChangeListener listener =
+                            affordabilityNote.getListener();
+                    listener.onAffordabilityChanged(userId, pkgName,
+                            affordabilityNote.getActionBill(),
+                            affordabilityNote.isCurrentlyAffordable());
+                }
+                break;
+
+                case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: {
+                    removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT);
+                    synchronized (mLock) {
+                        scheduleUnusedWealthReclamationLocked();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    private final class LocalService implements EconomyManagerInternal {
+        @Override
+        public void registerAffordabilityChangeListener(int userId, @NonNull String pkgName,
+                @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
+            synchronized (mLock) {
+                mAgent.registerAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
+            }
+        }
+
+        @Override
+        public void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName,
+                @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
+            synchronized (mLock) {
+                mAgent.unregisterAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
+            }
+        }
+
+        @Override
+        public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
+            // TODO: take temp-allowlist into consideration
+            long requiredBalance = 0;
+            final List<EconomyManagerInternal.AnticipatedAction> projectedActions =
+                    bill.getAnticipatedActions();
+            for (int i = 0; i < projectedActions.size(); ++i) {
+                AnticipatedAction action = projectedActions.get(i);
+                final long cost =
+                        mCompleteEconomicPolicy.getCostOfAction(action.actionId, userId, pkgName);
+                requiredBalance += cost * action.numInstantaneousCalls
+                        + cost * (action.ongoingDurationMs / 1000);
+            }
+            synchronized (mLock) {
+                return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance;
+            }
+        }
+
+        @Override
+        public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+                @Nullable String tag) {
+            synchronized (mLock) {
+                mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag);
+            }
+        }
+
+        @Override
+        public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+                @Nullable String tag) {
+            synchronized (mLock) {
+                final long nowElapsed = SystemClock.elapsedRealtime();
+                mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed);
+            }
+        }
+
+        @Override
+        public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+                @Nullable String tag) {
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            final long now = System.currentTimeMillis();
+            synchronized (mLock) {
+                mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now);
+            }
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
new file mode 100644
index 0000000..cce75f7
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * JobScheduler.
+ */
+public class JobSchedulerEconomicPolicy extends EconomicPolicy {
+    public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JS | 0;
+    public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JS | 1;
+    public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JS | 2;
+    public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JS | 3;
+    public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JS | 4;
+    public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JS | 5;
+    public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JS | 6;
+    public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JS | 7;
+    public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JS | 8;
+    public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9;
+    public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10;
+
+    private static final int[] COST_MODIFIERS = new int[]{
+            COST_MODIFIER_CHARGING,
+            COST_MODIFIER_POWER_SAVE_MODE,
+            COST_MODIFIER_PROCESS_STATE
+    };
+
+    private final SparseArray<Action> mActions = new SparseArray<>();
+    private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+    JobSchedulerEconomicPolicy(InternalResourceService irs) {
+        super(irs);
+        loadActions();
+        loadRewards();
+    }
+
+    @Override
+    long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+        // TODO: incorporate time since usage
+        return arcToNarc(2000);
+    }
+
+    @Override
+    long getMaxSatiatedBalance() {
+        return arcToNarc(60000);
+    }
+
+    @Override
+    long getMaxSatiatedCirculation() {
+        return arcToNarc(691200);
+    }
+
+    @NonNull
+    @Override
+    int[] getCostModifiers() {
+        return COST_MODIFIERS;
+    }
+
+    @Nullable
+    @Override
+    Action getAction(@AppAction int actionId) {
+        return mActions.get(actionId);
+    }
+
+    @Nullable
+    @Override
+    Reward getReward(@UtilityReward int rewardId) {
+        return mRewards.get(rewardId);
+    }
+
+    private void loadActions() {
+        mActions.put(ACTION_JOB_MAX_START,
+                new Action(ACTION_JOB_MAX_START, arcToNarc(3), arcToNarc(10)));
+        mActions.put(ACTION_JOB_MAX_RUNNING,
+                new Action(ACTION_JOB_MAX_RUNNING, arcToNarc(2), arcToNarc(5)));
+        mActions.put(ACTION_JOB_HIGH_START,
+                new Action(ACTION_JOB_HIGH_START, arcToNarc(3), arcToNarc(8)));
+        mActions.put(ACTION_JOB_HIGH_RUNNING,
+                new Action(ACTION_JOB_HIGH_RUNNING, arcToNarc(2), arcToNarc(4)));
+        mActions.put(ACTION_JOB_DEFAULT_START,
+                new Action(ACTION_JOB_DEFAULT_START, arcToNarc(3), arcToNarc(6)));
+        mActions.put(ACTION_JOB_DEFAULT_RUNNING,
+                new Action(ACTION_JOB_DEFAULT_RUNNING, arcToNarc(2), arcToNarc(3)));
+        mActions.put(ACTION_JOB_LOW_START,
+                new Action(ACTION_JOB_LOW_START, arcToNarc(3), arcToNarc(4)));
+        mActions.put(ACTION_JOB_LOW_RUNNING,
+                new Action(ACTION_JOB_LOW_RUNNING, arcToNarc(2), arcToNarc(2)));
+        mActions.put(ACTION_JOB_MIN_START,
+                new Action(ACTION_JOB_MIN_START, arcToNarc(3), arcToNarc(2)));
+        mActions.put(ACTION_JOB_MIN_RUNNING,
+                new Action(ACTION_JOB_MIN_RUNNING, arcToNarc(2), arcToNarc(1)));
+        mActions.put(ACTION_JOB_TIMEOUT,
+                new Action(ACTION_JOB_TIMEOUT, arcToNarc(30), arcToNarc(60)));
+    }
+
+    private void loadRewards() {
+        mRewards.put(REWARD_TOP_ACTIVITY,
+                new Reward(REWARD_TOP_ACTIVITY,
+                        arcToNarc(0), /* .5 arcs */ arcToNarc(5) / 10, arcToNarc(15000)));
+        mRewards.put(REWARD_NOTIFICATION_SEEN,
+                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(1), arcToNarc(0), arcToNarc(10)));
+        mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+                new Reward(REWARD_NOTIFICATION_INTERACTION,
+                        arcToNarc(5), arcToNarc(0), arcToNarc(5000)));
+        mRewards.put(REWARD_WIDGET_INTERACTION,
+                new Reward(REWARD_WIDGET_INTERACTION,
+                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+        mRewards.put(REWARD_OTHER_USER_INTERACTION,
+                new Reward(REWARD_OTHER_USER_INTERACTION,
+                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
new file mode 100644
index 0000000..ae1bf26
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.dumpTime;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.SparseLongArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Ledger to track the last recorded balance and recent activities of an app.
+ */
+class Ledger {
+    static class Transaction {
+        public final long startTimeMs;
+        public final long endTimeMs;
+        public final int eventId;
+        @Nullable
+        public final String tag;
+        public final long delta;
+
+        Transaction(long startTimeMs, long endTimeMs,
+                int eventId, @Nullable String tag, long delta) {
+            this.startTimeMs = startTimeMs;
+            this.endTimeMs = endTimeMs;
+            this.eventId = eventId;
+            this.tag = tag;
+            this.delta = delta;
+        }
+    }
+
+    /** Last saved balance. This doesn't take currently ongoing events into account. */
+    private long mCurrentBalance = 0;
+    private final List<Transaction> mTransactions = new ArrayList<>();
+    private final SparseLongArray mCumulativeDeltaPerReason = new SparseLongArray();
+    private long mEarliestSumTime;
+
+    Ledger() {
+    }
+
+    long getCurrentBalance() {
+        return mCurrentBalance;
+    }
+
+    @Nullable
+    Transaction getEarliestTransaction() {
+        if (mTransactions.size() > 0) {
+            return mTransactions.get(0);
+        }
+        return null;
+    }
+
+    void recordTransaction(@NonNull Transaction transaction) {
+        mTransactions.add(transaction);
+        mCurrentBalance += transaction.delta;
+
+        final long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+        mCumulativeDeltaPerReason.put(transaction.eventId, sum + transaction.delta);
+        mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs);
+    }
+
+    long get24HourSum(int eventId, final long now) {
+        final long windowStartTime = now - 24 * HOUR_IN_MILLIS;
+        if (mEarliestSumTime < windowStartTime) {
+            // Need to redo sums
+            mCumulativeDeltaPerReason.clear();
+            for (int i = mTransactions.size() - 1; i >= 0; --i) {
+                final Transaction transaction = mTransactions.get(i);
+                if (transaction.endTimeMs <= windowStartTime) {
+                    break;
+                }
+                long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+                if (transaction.startTimeMs >= windowStartTime) {
+                    sum += transaction.delta;
+                } else {
+                    // Pro-rate durationed deltas. Intentionally floor the result.
+                    sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime)
+                            * transaction.delta)
+                            / (transaction.endTimeMs - transaction.startTimeMs);
+                }
+                mCumulativeDeltaPerReason.put(transaction.eventId, sum);
+            }
+            mEarliestSumTime = windowStartTime;
+        }
+        return mCumulativeDeltaPerReason.get(eventId);
+    }
+
+    /** Deletes transactions that are older than {@code minAgeMs}. */
+    void removeOldTransactions(long minAgeMs) {
+        final long cutoff = System.currentTimeMillis() - minAgeMs;
+        while (mTransactions.get(0).endTimeMs <= cutoff) {
+            mTransactions.remove(0);
+        }
+    }
+
+    void dump(IndentingPrintWriter pw) {
+        pw.print("Current balance", narcToString(getCurrentBalance())).println();
+        mTransactions.forEach((transaction) -> {
+            dumpTime(pw, transaction.startTimeMs);
+            pw.print("--");
+            dumpTime(pw, transaction.endTimeMs);
+            pw.print(": ");
+            pw.print(EconomicPolicy.eventToString(transaction.eventId));
+            pw.print(" --> ");
+            pw.println(narcToString(transaction.delta));
+        });
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
new file mode 100644
index 0000000..6b89b79
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.IntDef;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class of a modifier that can affect end pricing.
+ */
+abstract class Modifier {
+    static final int COST_MODIFIER_CHARGING = 0;
+    static final int COST_MODIFIER_DEVICE_IDLE = 1;
+    static final int COST_MODIFIER_POWER_SAVE_MODE = 2;
+    static final int COST_MODIFIER_PROCESS_STATE = 3;
+    static final int NUM_COST_MODIFIERS = COST_MODIFIER_PROCESS_STATE + 1;
+
+    @IntDef({
+            COST_MODIFIER_CHARGING,
+            COST_MODIFIER_DEVICE_IDLE,
+            COST_MODIFIER_POWER_SAVE_MODE,
+            COST_MODIFIER_PROCESS_STATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CostModifier {
+    }
+
+    /**
+     * Returns a modified cost to produce based on the modifier's state.
+     *
+     * @param ctp Current cost to produce
+     */
+    long getModifiedCostToProduce(long ctp) {
+        return ctp;
+    }
+
+    /**
+     * Returns a modified price based on the modifier's state.
+     *
+     * @param price Current price
+     */
+    long getModifiedPrice(long price) {
+        return price;
+    }
+
+    void onSystemServicesReady() {
+    }
+
+    abstract void dump(IndentingPrintWriter pw);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..96ec75f
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
@@ -0,0 +1,5 @@
+dplotnikov@google.com
+kwekua@google.com
+mwachens@google.com
+suprabh@google.com
+yamasani@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
new file mode 100644
index 0000000..764a3a8
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+/** Modifier that makes things more expensive in adaptive and full battery saver are active. */
+class PowerSaveModeModifier extends Modifier {
+    private final InternalResourceService mIrs;
+    private final PowerManager mPowerManager;
+
+    PowerSaveModeModifier(@NonNull InternalResourceService irs) {
+        super();
+        mIrs = irs;
+        mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+    }
+
+    @Override
+    long getModifiedCostToProduce(long ctp) {
+        if (mPowerManager.isPowerSaveMode()) {
+            return (long) (1.5 * ctp);
+        }
+        // TODO: get adaptive power save mode
+        if (mPowerManager.isPowerSaveMode()) {
+            return (long) (1.25 * ctp);
+        }
+        return ctp;
+    }
+
+    @Override
+    void dump(IndentingPrintWriter pw) {
+        pw.print("power save=");
+        pw.println(mPowerManager.isPowerSaveMode());
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
new file mode 100644
index 0000000..2ee6459
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Modifier that makes things more cheaper based on an app's process state. */
+class ProcessStateModifier extends Modifier {
+    private static final String TAG = "TARE-" + ProcessStateModifier.class.getSimpleName();
+
+    private static final int PROC_STATE_BUCKET_NONE = 0;
+    private static final int PROC_STATE_BUCKET_TOP = 1;
+    private static final int PROC_STATE_BUCKET_FGS = 2;
+    private static final int PROC_STATE_BUCKET_BFGS = 3;
+    private static final int PROC_STATE_BUCKET_BG = 4;
+
+    @IntDef(prefix = {"PROC_STATE_BUCKET_"}, value = {
+            PROC_STATE_BUCKET_NONE,
+            PROC_STATE_BUCKET_TOP,
+            PROC_STATE_BUCKET_FGS,
+            PROC_STATE_BUCKET_BFGS,
+            PROC_STATE_BUCKET_BG
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcStateBucket {
+    }
+
+    private final Object mLock = new Object();
+    private final InternalResourceService mIrs;
+    private final PackageManagerInternal mPackageManagerInternal;
+
+    /** Cached mapping of userId+package to their UIDs (for all users) */
+    private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+
+    @GuardedBy("mLock")
+    private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
+
+    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+            final int newBucket = getProcStateBucket(procState);
+            synchronized (mLock) {
+                final int curBucket = mUidProcStateBucketCache.get(uid);
+                if (curBucket != newBucket) {
+                    mUidProcStateBucketCache.put(uid, newBucket);
+                }
+                notifyStateChangedLocked(uid);
+            }
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+            synchronized (mLock) {
+                if (mUidProcStateBucketCache.indexOfKey(uid) < 0) {
+                    Slog.e(TAG, "UID " + uid + " marked gone but wasn't in cache.");
+                    return;
+                }
+                mUidProcStateBucketCache.delete(uid);
+                notifyStateChangedLocked(uid);
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid) {
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) {
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) {
+        }
+    };
+
+    ProcessStateModifier(@NonNull InternalResourceService irs) {
+        super();
+        mIrs = irs;
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void onSystemServicesReady() {
+        try {
+            ActivityManager.getService().registerUidObserver(mUidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
+        } catch (RemoteException e) {
+            // ignored; both services live in system_server
+        }
+    }
+
+    /**
+     * Get the final modified price based on an app's process state.
+     *
+     * @param ctp   Cost to produce. @see EconomicPolicy.Action#costToProduce
+     * @param price Current price
+     */
+    long getModifiedPrice(final int userId, @NonNull final String pkgName,
+            final long ctp, final long price) {
+        final int procState;
+        synchronized (mLock) {
+            procState = mUidProcStateBucketCache.get(
+                    getUidLocked(userId, pkgName), PROC_STATE_BUCKET_NONE);
+        }
+        switch (procState) {
+            case PROC_STATE_BUCKET_TOP:
+                return 0;
+            case PROC_STATE_BUCKET_FGS:
+                // Can't get notification priority. Just use CTP for now.
+                return ctp;
+            case PROC_STATE_BUCKET_BFGS:
+                return (long) (ctp + .5 * (price - ctp));
+            case PROC_STATE_BUCKET_BG:
+            default:
+                return price;
+        }
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void dump(IndentingPrintWriter pw) {
+        pw.print("Proc state bucket cache = ");
+        pw.println(mUidProcStateBucketCache);
+    }
+
+    @GuardedBy("mLock")
+    private int getUidLocked(final int userId, @NonNull final String pkgName) {
+        if (!mPackageToUidCache.contains(userId, pkgName)) {
+            mPackageToUidCache.add(userId, pkgName,
+                    mPackageManagerInternal.getPackageUid(pkgName, 0, userId));
+        }
+        return mPackageToUidCache.get(userId, pkgName);
+    }
+
+    @ProcStateBucket
+    private int getProcStateBucket(int procState) {
+        if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+            return PROC_STATE_BUCKET_TOP;
+        }
+        if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            return PROC_STATE_BUCKET_FGS;
+        }
+        if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            return PROC_STATE_BUCKET_BFGS;
+        }
+        return PROC_STATE_BUCKET_BG;
+    }
+
+    @GuardedBy("mLock")
+    private void notifyStateChangedLocked(final int uid) {
+        // Never call out to the IRS with the local lock held.
+        TareHandlerThread.getHandler().post(() -> mIrs.onUidStateChanged(uid));
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
new file mode 100644
index 0000000..73b00b6
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+    "presubmit": [
+        {
+            "name": "FrameworksMockingServicesTests",
+            "options": [
+                {"include-filter": "com.android.server.tare"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+                {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+            ]
+        },
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {"include-filter": "com.android.server.tare"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+                {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+            ]
+        }
+    ],
+    "postsubmit": [
+        {
+            "name": "FrameworksMockingServicesTests",
+            "options": [
+                {"include-filter": "com.android.server.tare"}
+            ]
+        },
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {"include-filter": "com.android.server.tare"}
+            ]
+        }
+    ]
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
new file mode 100644
index 0000000..5b2b660
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
+
+/**
+ * Singleton thread for all of TARE.
+ *
+ * @see com.android.internal.os.BackgroundThread
+ */
+final class TareHandlerThread extends HandlerThread {
+
+    private static TareHandlerThread sInstance;
+    private static Handler sHandler;
+
+    private TareHandlerThread() {
+        super("tare");
+    }
+
+    private static void ensureThreadLocked() {
+        if (sInstance == null) {
+            sInstance = new TareHandlerThread();
+            sInstance.start();
+            final Looper looper = sInstance.getLooper();
+            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+            sHandler = new Handler(sInstance.getLooper());
+        }
+    }
+
+    static TareHandlerThread get() {
+        synchronized (TareHandlerThread.class) {
+            ensureThreadLocked();
+        }
+        return sInstance;
+    }
+
+    /** Returns the singleton handler for TareHandlerThread. */
+    public static Handler getHandler() {
+        synchronized (TareHandlerThread.class) {
+            ensureThreadLocked();
+        }
+        return sHandler;
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
new file mode 100644
index 0000000..2d72f56
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.util.IndentingPrintWriter;
+
+import java.text.SimpleDateFormat;
+
+class TareUtils {
+    private static final long NARC_IN_ARC = 1_000_000_000L;
+
+    @SuppressLint("SimpleDateFormat")
+    private static final SimpleDateFormat sDumpDateFormat =
+            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+    static long arcToNarc(int arcs) {
+        return arcs * NARC_IN_ARC;
+    }
+
+    static void dumpTime(IndentingPrintWriter pw, long time) {
+        pw.print(sDumpDateFormat.format(time));
+    }
+
+    static int narcToArc(long narcs) {
+        return (int) (narcs / NARC_IN_ARC);
+    }
+
+    @NonNull
+    static String narcToString(long narcs) {
+        if (narcs == 0) {
+            return "0 ARCs";
+        }
+        final long sub = Math.abs(narcs) % NARC_IN_ARC;
+        final long arcs = narcToArc(narcs);
+        if (arcs == 0) {
+            return sub + " narcs";
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(arcs);
+        if (sub > 0) {
+            sb.append(".").append(sub / (NARC_IN_ARC / 1000));
+        }
+        sb.append(" ARCs");
+        return sb.toString();
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index d532e20..187422b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -462,6 +462,17 @@
         return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
     }
 
+    public long getTimeSinceLastUsedByUser(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory =
+                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+        if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE
+                || appUsageHistory.lastUsedByUserElapsedTime == 0) {
+            return Long.MAX_VALUE;
+        }
+        return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime;
+    }
+
     public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 4b081d2..2fa10f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1082,6 +1082,14 @@
     }
 
     @Override
+    public long getTimeSinceLastUsedByUser(String packageName, int userId) {
+        final long elapsedRealtime = mInjector.elapsedRealtime();
+        synchronized (mAppIdleLock) {
+            return mAppIdleHistory.getTimeSinceLastUsedByUser(packageName, userId, elapsedRealtime);
+        }
+    }
+
+    @Override
     public void onUserRemoved(int userId) {
         synchronized (mAppIdleLock) {
             mAppIdleHistory.onUserRemoved(userId);
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c9087..3d21914 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "CtsMediaParserTestCases"
     },
     {
-      "name": "CtsMediaParserHostSideTestCases"
+      "name": "CtsMediaParserHostTestCases"
     }
   ]
 }
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
index 26f4568..7e71591 100644
--- a/apex/media/framework/java/android/media/Session2Command.java
+++ b/apex/media/framework/java/android/media/Session2Command.java
@@ -37,9 +37,8 @@
  * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
  * {@link #getCustomAction()} shouldn't be {@code null}.
  * <p>
- * Refer to the
- * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
- * class for the list of valid commands.
+ * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html">
+ * AndroidX SessionCommand</a> class for the list of valid commands.
  */
 public final class Session2Command implements Parcelable {
     /**
diff --git a/boot/Android.bp b/boot/Android.bp
index e8d88a5..b71f9bf 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -76,6 +76,10 @@
             module: "com.android.mediaprovider-bootclasspath-fragment",
         },
         {
+            apex: "com.android.nearby",
+            module: "com.android.nearby-bootclasspath-fragment",
+        },
+        {
             apex: "com.android.os.statsd",
             module: "com.android.os.statsd-bootclasspath-fragment",
         },
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index de1bcae..bdbbe2a 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -35,8 +35,10 @@
     tidy_checks_as_errors: [
         "modernize-*",
         "-modernize-avoid-c-arrays",
+        "-modernize-concat-nested-namespaces",
         "-modernize-pass-by-value",
         "-modernize-replace-disallow-copy-and-assign-macro",
+        "-modernize-return-braced-init-list",
         "-modernize-use-equals-default",
         "-modernize-use-nodiscard",
         "-modernize-use-override",
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d4da5e5..7e6a521 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -45,13 +45,13 @@
 #define COLORSPACE_SRGB       1
 #define COLORSPACE_DISPLAY_P3 2
 
-static void usage(const char* pname, PhysicalDisplayId displayId)
+static void usage(const char* pname, DisplayId displayId)
 {
     fprintf(stderr,
             "usage: %s [-hp] [-d display-id] [FILENAME]\n"
             "   -h: this message\n"
             "   -p: save the file as a png.\n"
-            "   -d: specify the physical display ID to capture (default: %s)\n"
+            "   -d: specify the display ID to capture (default: %s)\n"
             "       see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
             "If FILENAME ends with .png it will be saved as a png.\n"
             "If FILENAME is not given, the results will be printed to stdout.\n",
@@ -121,9 +121,9 @@
 
 int main(int argc, char** argv)
 {
-    std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+    std::optional<DisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
     if (!displayId) {
-        fprintf(stderr, "Failed to get token for internal display\n");
+        fprintf(stderr, "Failed to get ID for internal display\n");
         return 1;
     }
 
@@ -136,7 +136,11 @@
                 png = true;
                 break;
             case 'd':
-                displayId = PhysicalDisplayId(atoll(optarg));
+                displayId = DisplayId::fromValue(atoll(optarg));
+                if (!displayId) {
+                    fprintf(stderr, "Invalid display ID\n");
+                    return 1;
+                }
                 break;
             case '?':
             case 'h':
@@ -182,7 +186,7 @@
     ProcessState::self()->startThreadPool();
 
     sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
+    status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
diff --git a/config/OWNERS b/config/OWNERS
index 0691dbc..c0778f8 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,8 +1,8 @@
 include /ZYGOTE_OWNERS
 
 # art-team@ manages the boot image profiles
-per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file boot-* = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file dirty-image-objects = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file generate-preloaded-classes.sh = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file preloaded-classes* = calin@google.com, ngeoffray@google.com, vmarko@google.com
 
diff --git a/core/api/current.txt b/core/api/current.txt
index 1de47b5..a0489d1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7455,7 +7455,7 @@
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
     field public static final int KEYGUARD_DISABLE_IRIS = 256; // 0x100
-    field public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
+    field @Deprecated public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
     field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
     field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
     field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
@@ -7937,6 +7937,7 @@
     method public static final long getMinFlexMillis();
     method public long getMinLatencyMillis();
     method public static final long getMinPeriodMillis();
+    method public long getMinimumNetworkChunkBytes();
     method @Deprecated public int getNetworkType();
     method @Nullable public android.net.NetworkRequest getRequiredNetwork();
     method @NonNull public android.content.ComponentName getService();
@@ -7979,6 +7980,7 @@
     method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
     method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
+    method @NonNull public android.app.job.JobInfo.Builder setMinimumNetworkChunkBytes(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
@@ -8071,11 +8073,13 @@
   public final class JobWorkItem implements android.os.Parcelable {
     ctor public JobWorkItem(android.content.Intent);
     ctor public JobWorkItem(android.content.Intent, long, long);
+    ctor public JobWorkItem(@Nullable android.content.Intent, long, long, long);
     method public int describeContents();
     method public int getDeliveryCount();
     method public long getEstimatedNetworkDownloadBytes();
     method public long getEstimatedNetworkUploadBytes();
     method public android.content.Intent getIntent();
+    method public long getMinimumNetworkChunkBytes();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
   }
@@ -9429,6 +9433,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice);
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
   }
 
@@ -9453,6 +9458,7 @@
     field @Deprecated public static final int HEALTH = 3; // 0x3
     field public static final int HEARING_AID = 21; // 0x15
     field public static final int HID_DEVICE = 19; // 0x13
+    field public static final int LE_AUDIO = 22; // 0x16
     field public static final int SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
@@ -11175,11 +11181,13 @@
     field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
     field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
     field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+    field public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
     field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
     field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
     field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
     field public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
     field public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+    field public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
     field public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
     field public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
     field public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
@@ -11372,6 +11380,8 @@
     method public final void addDataScheme(String);
     method public final void addDataSchemeSpecificPart(String, int);
     method public final void addDataType(String) throws android.content.IntentFilter.MalformedMimeTypeException;
+    method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicate();
+    method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicateWithTypeResolution(@NonNull android.content.ContentResolver);
     method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
     method public final java.util.Iterator<java.lang.String> categoriesIterator();
     method public final int countActions();
@@ -12545,6 +12555,7 @@
     method public abstract int getInstantAppCookieMaxBytes();
     method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String);
+    method @NonNull public android.content.IntentSender getLaunchIntentSenderForPackage(@NonNull String);
     method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String);
     method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
     method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -15254,9 +15265,11 @@
     method public void clearContent();
     method @NonNull public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
     method public void destroy();
+    method public static boolean isDrawingEnabled();
     method public boolean isOpaque();
     method public void notifyFramePending();
     method public void setContentRoot(@Nullable android.graphics.RenderNode);
+    method public static void setDrawingEnabled(boolean);
     method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
     method public void setLightSourceGeometry(float, float, float, float);
     method public void setName(@NonNull String);
@@ -20336,24 +20349,45 @@
     field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
     field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
     field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
+    field public static final int CHANNEL_OUT_5POINT1POINT2 = 3145980; // 0x3000fc
+    field public static final int CHANNEL_OUT_5POINT1POINT4 = 737532; // 0xb40fc
     field @Deprecated public static final int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+    field public static final int CHANNEL_OUT_7POINT1POINT2 = 3152124; // 0x3018fc
+    field public static final int CHANNEL_OUT_7POINT1POINT4 = 743676; // 0xb58fc
     field public static final int CHANNEL_OUT_7POINT1_SURROUND = 6396; // 0x18fc
+    field public static final int CHANNEL_OUT_9POINT1POINT4 = 202070268; // 0xc0b58fc
+    field public static final int CHANNEL_OUT_9POINT1POINT6 = 205215996; // 0xc3b58fc
     field public static final int CHANNEL_OUT_BACK_CENTER = 1024; // 0x400
     field public static final int CHANNEL_OUT_BACK_LEFT = 64; // 0x40
     field public static final int CHANNEL_OUT_BACK_RIGHT = 128; // 0x80
+    field public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 8388608; // 0x800000
+    field public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 4194304; // 0x400000
+    field public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 16777216; // 0x1000000
     field public static final int CHANNEL_OUT_DEFAULT = 1; // 0x1
     field public static final int CHANNEL_OUT_FRONT_CENTER = 16; // 0x10
     field public static final int CHANNEL_OUT_FRONT_LEFT = 4; // 0x4
     field public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 256; // 0x100
     field public static final int CHANNEL_OUT_FRONT_RIGHT = 8; // 0x8
     field public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 512; // 0x200
+    field public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 67108864; // 0x4000000
+    field public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 134217728; // 0x8000000
     field public static final int CHANNEL_OUT_LOW_FREQUENCY = 32; // 0x20
+    field public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 33554432; // 0x2000000
     field public static final int CHANNEL_OUT_MONO = 4; // 0x4
     field public static final int CHANNEL_OUT_QUAD = 204; // 0xcc
     field public static final int CHANNEL_OUT_SIDE_LEFT = 2048; // 0x800
     field public static final int CHANNEL_OUT_SIDE_RIGHT = 4096; // 0x1000
     field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
     field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
+    field public static final int CHANNEL_OUT_TOP_BACK_CENTER = 262144; // 0x40000
+    field public static final int CHANNEL_OUT_TOP_BACK_LEFT = 131072; // 0x20000
+    field public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 524288; // 0x80000
+    field public static final int CHANNEL_OUT_TOP_CENTER = 8192; // 0x2000
+    field public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 32768; // 0x8000
+    field public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 16384; // 0x4000
+    field public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 65536; // 0x10000
+    field public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 1048576; // 0x100000
+    field public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 2097152; // 0x200000
     field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
     field public static final int ENCODING_AAC_ELD = 15; // 0xf
     field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
@@ -30797,6 +30831,7 @@
     field public static final int Q = 29; // 0x1d
     field public static final int R = 30; // 0x1e
     field public static final int S = 31; // 0x1f
+    field public static final int TIRAMISU = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -31070,8 +31105,8 @@
     ctor public Environment();
     method public static java.io.File getDataDirectory();
     method public static java.io.File getDownloadCacheDirectory();
-    method @Deprecated public static java.io.File getExternalStorageDirectory();
-    method @Deprecated public static java.io.File getExternalStoragePublicDirectory(String);
+    method public static java.io.File getExternalStorageDirectory();
+    method public static java.io.File getExternalStoragePublicDirectory(String);
     method public static String getExternalStorageState();
     method public static String getExternalStorageState(java.io.File);
     method @NonNull public static java.io.File getRootDirectory();
@@ -45958,6 +45993,7 @@
     method public boolean contains(Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void ensureCapacity(int);
+    method public void forEach(java.util.function.Consumer<? super E>);
     method public int indexOf(Object);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
@@ -51019,6 +51055,7 @@
     method public CharSequence getClassName();
     method public CharSequence getContentDescription();
     method public int getCurrentItemIndex();
+    method public int getDisplayId();
     method public int getFromIndex();
     method public int getItemCount();
     method public int getMaxScrollX();
@@ -51435,6 +51472,7 @@
 
   public final class AutofillManager {
     method public void cancel();
+    method public void clearAutofillRequestCallback();
     method public void commit();
     method public void disableAutofillServices();
     method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -51460,6 +51498,7 @@
     method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     method public void requestAutofill(@NonNull android.view.View);
     method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
+    method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
     method public void setUserData(@Nullable android.service.autofill.UserData);
     method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
@@ -51478,6 +51517,10 @@
     field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
   }
 
+  public interface AutofillRequestCallback {
+    method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
+  }
+
   public final class AutofillValue implements android.os.Parcelable {
     method public int describeContents();
     method public static android.view.autofill.AutofillValue forDate(long);
@@ -51848,10 +51891,12 @@
     ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
   }
 
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e48a1da..ef9df660 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,14 @@
 
 }
 
+package android.location {
+
+  public class LocationManager {
+    method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull android.location.Location);
+  }
+
+}
+
 package android.media {
 
   public class AudioManager {
@@ -266,6 +274,10 @@
     field public static final int VPN_UID = 1016; // 0x3f8
   }
 
+  public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+    method @NonNull public static android.os.SharedMemory create(@NonNull android.os.ParcelFileDescriptor);
+  }
+
   public class StatsServiceManager {
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
old mode 100644
new mode 100755
index 2d73aa67..7a651c6
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -924,6 +924,7 @@
   }
 
   public class DevicePolicyManager {
+    method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -956,7 +957,7 @@
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
     field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
-    field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
+    field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -972,6 +973,7 @@
     field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
     field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
     field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4
+    field public static final int PROVISIONING_TRIGGER_NFC = 5; // 0x5
     field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
     field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
     field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
@@ -2087,6 +2089,7 @@
     field public static final int PBAP_CLIENT = 17; // 0x11
     field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
     field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
+    field public static final int VOLUME_CONTROL = 23; // 0x17
   }
 
   public final class BluetoothStatusCodes {
@@ -2127,6 +2130,16 @@
     field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
   }
 
+  public final class BluetoothVolumeControl implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@Nullable android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+  }
+
   public final class BufferConstraint implements android.os.Parcelable {
     ctor public BufferConstraint(int, int, int);
     method public int describeContents();
@@ -2202,17 +2215,17 @@
   public final class BluetoothLeScanner {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
   }
 
-  public final class ResultStorageDescriptor implements android.os.Parcelable {
-    ctor public ResultStorageDescriptor(int, int, int);
-    method public int describeContents();
-    method public int getLength();
-    method public int getOffset();
-    method public int getType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
+  @Deprecated public final class ResultStorageDescriptor implements android.os.Parcelable {
+    ctor @Deprecated public ResultStorageDescriptor(int, int, int);
+    method @Deprecated public int describeContents();
+    method @Deprecated public int getLength();
+    method @Deprecated public int getOffset();
+    method @Deprecated public int getType();
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
   }
 
   public final class ScanFilter implements android.os.Parcelable {
@@ -2236,10 +2249,10 @@
     method public android.bluetooth.le.ScanSettings.Builder setScanResultType(int);
   }
 
-  public final class TruncatedFilter {
-    ctor public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
-    method public android.bluetooth.le.ScanFilter getFilter();
-    method public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
+  @Deprecated public final class TruncatedFilter {
+    ctor @Deprecated public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
+    method @Deprecated public android.bluetooth.le.ScanFilter getFilter();
+    method @Deprecated public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
   }
 
 }
@@ -2848,7 +2861,7 @@
     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 @Deprecated 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_KNOWN_SIGNER = 134217728; // 0x8000000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
@@ -3297,7 +3310,9 @@
     method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerStateChangeOnActiveSourceLost();
+    method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getRoutingControl();
     method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+    method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioControl();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
     method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
@@ -3313,7 +3328,9 @@
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setRoutingControl(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioControl(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
@@ -3323,6 +3340,8 @@
     field public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
     field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
     field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
+    field public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+    field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL = "system_audio_control";
     field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
     field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
     field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
@@ -3380,6 +3399,7 @@
     field public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
     field public static final String POWER_CONTROL_MODE_NONE = "none";
     field public static final String POWER_CONTROL_MODE_TV = "to_tv";
+    field public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
     field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
     field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
     field public static final int POWER_STATUS_ON = 0; // 0x0
@@ -3395,6 +3415,10 @@
     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 ROUTING_CONTROL_DISABLED = 0; // 0x0
+    field public static final int ROUTING_CONTROL_ENABLED = 1; // 0x1
+    field public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0; // 0x0
+    field public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1; // 0x1
     field public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; // 0x0
     field public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1; // 0x1
     field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
@@ -3660,7 +3684,7 @@
   public class ContextHubClient implements java.io.Closeable {
     method public void close();
     method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
-    method @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
   }
 
   public class ContextHubClientCallback {
@@ -3711,26 +3735,26 @@
   }
 
   public final class ContextHubManager {
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
-    method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] getContextHubHandles();
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
-    method @Deprecated @Nullable @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] getContextHubHandles();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
     method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
     method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
     method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
     field public static final int AUTHORIZATION_DENIED = 0; // 0x0
     field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
@@ -4909,6 +4933,7 @@
 
   public final class LastLocationRequest implements android.os.Parcelable {
     method public int describeContents();
+    method public boolean isAdasGnssBypass();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -4919,6 +4944,7 @@
     ctor public LastLocationRequest.Builder();
     ctor public LastLocationRequest.Builder(@NonNull android.location.LastLocationRequest);
     method @NonNull public android.location.LastLocationRequest build();
+    method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setAdasGnssBypass(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LastLocationRequest.Builder setHiddenFromAppOps(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
   }
@@ -4938,6 +4964,7 @@
     method @Deprecated public int getGnssBatchSize();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String, @NonNull android.location.LastLocationRequest);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
+    method public boolean isAdasGnssLocationEnabled();
     method public boolean isExtraLocationControllerPackageEnabled();
     method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -4949,11 +4976,14 @@
     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 setAdasGnssLocationEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
+    field public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED = "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+    field public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
   }
 
   public final class LocationRequest implements android.os.Parcelable {
@@ -4969,6 +4999,7 @@
     method @Deprecated @NonNull public String getProvider();
     method @Deprecated public float getSmallestDisplacement();
     method @NonNull public android.os.WorkSource getWorkSource();
+    method public boolean isAdasGnssBypass();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
@@ -4994,6 +5025,7 @@
   }
 
   public static final class LocationRequest.Builder {
+    method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setAdasGnssBypass(boolean);
     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);
@@ -5388,6 +5420,8 @@
     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 MIX_TYPE_PLAYERS = 0; // 0x0
+    field public static final int MIX_TYPE_RECORDERS = 1; // 0x1
     field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2
     field public static final int ROUTE_FLAG_RENDER = 1; // 0x1
   }
@@ -5401,6 +5435,7 @@
   }
 
   public class AudioMixingRule {
+    method public int getTargetMixType();
     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
@@ -5415,6 +5450,7 @@
     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;
+    method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder setTargetMixType(int);
   }
 
   public class AudioPolicy {
@@ -8059,8 +8095,12 @@
   }
 
   public final class BatteryStatsManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanReset();
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanResults(@NonNull android.os.WorkSource, int);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStarted(@NonNull android.os.WorkSource, boolean);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStopped(@NonNull android.os.WorkSource, boolean);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
@@ -9053,6 +9093,7 @@
 
   public final class DeviceConfig {
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
@@ -9105,6 +9146,8 @@
     field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
     field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
     field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+    field public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT = "surface_flinger_native_boot";
+    field public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
     field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
     field public static final String NAMESPACE_TELEPHONY = "telephony";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a079939..fb481eb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1281,6 +1281,12 @@
 
 package android.inputmethodservice {
 
+  public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback {
+    method public final int getInitialDisplayId();
+    method @Nullable public final android.os.Bundle getWindowContextOptions();
+    method public final int getWindowType();
+  }
+
   @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
   }
@@ -1397,6 +1403,7 @@
 package android.media {
 
   public final class AudioAttributes implements android.os.Parcelable {
+    method public static int[] getSdkUsages();
     method @NonNull public static String usageToXsdString(int);
     method public static int xsdStringToUsage(@NonNull String);
   }
@@ -1421,7 +1428,9 @@
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
     method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
     method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+    method public static final int[] getPublicStreamTypes();
     method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+    method public int getStreamMinVolumeInt(int);
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
     method public boolean hasRegisteredDynamicPolicy();
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
@@ -1446,6 +1455,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance();
     method public static final int getNumStreamTypes();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float);
+    method @NonNull public static String streamToString(int);
     field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
     field public static final int DEVICE_ROLE_NONE = 0; // 0x0
     field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
@@ -1543,6 +1553,13 @@
     method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
   }
 
+  public final class AudioProductStrategy implements android.os.Parcelable {
+    method @NonNull public static android.media.AudioAttributes getDefaultAttributes();
+    method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+    method public int getVolumeGroupIdForAudioAttributes(@NonNull android.media.AudioAttributes);
+    method public int getVolumeGroupIdForLegacyStreamType(int);
+  }
+
 }
 
 package android.media.metrics {
@@ -2121,6 +2138,7 @@
   public final class DeviceConfig {
     field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
     field public static final String NAMESPACE_ANDROID = "android";
+    field public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
     field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
     field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
     field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
@@ -2865,6 +2883,10 @@
     method public long getAccessibilityIdForRegion(@NonNull android.graphics.Region);
   }
 
+  public class AccessibilityRecord {
+    method public void setDisplayId(int);
+  }
+
   public final class AccessibilityWindowInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
   }
@@ -3250,13 +3272,14 @@
 
   public class WindowOrganizer {
     ctor public WindowOrganizer();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
+    method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
+    method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
   }
 
   @UiContext public abstract class WindowProviderService extends android.app.Service {
     ctor public WindowProviderService();
     method public final void attachToWindowToken(@NonNull android.os.IBinder);
+    method @NonNull public int getInitialDisplayId();
     method @Nullable public android.os.Bundle getWindowContextOptions();
     method public abstract int getWindowType();
   }
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 5409165..e3690e5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -71,6 +71,10 @@
     
 
 
+AllUpper: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+    Constant field names must be named with only upper case characters: `android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes`, should be `S_DEFAULT_ATTRIBUTES`?
+
+
 ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
     
 ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
@@ -537,6 +541,8 @@
     
 
 
+InternalField: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+    Internal field sDefaultAttributes must not be exposed
 InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
     
 
@@ -1045,8 +1051,14 @@
     
 MissingNullability: android.location.LocationRequest#writeToParcel(android.os.Parcel, int) parameter #0:
     
+MissingNullability: android.media.AudioAttributes#SDK_USAGES:
+    Missing nullability on field `SDK_USAGES` in class `class android.media.AudioAttributes`
+MissingNullability: android.media.AudioAttributes#getSdkUsages():
+    Missing nullability on method `getSdkUsages` return
 MissingNullability: android.media.AudioFocusInfo#writeToParcel(android.os.Parcel, int) parameter #0:
     
+MissingNullability: android.media.AudioManager#getPublicStreamTypes():
+    Missing nullability on method `getPublicStreamTypes` return
 MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
     
 MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
@@ -1063,6 +1075,8 @@
     
 MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
     
+MissingNullability: android.media.AudioSystem#streamToString(int):
+    Missing nullability on method `streamToString` return
 MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
     
 MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 6c001f3..89d93ae 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -313,6 +313,10 @@
     backend: {
         rust: {
             enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.virt", // for virtualizationservice
+            ],
         },
     },
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e91209c..f47995c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,8 @@
 
 package android.accessibilityservice;
 
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
 import android.accessibilityservice.GestureDescription.MotionEventGenerator;
 import android.annotation.CallbackExecutor;
 import android.annotation.ColorInt;
@@ -27,6 +29,7 @@
 import android.app.Service;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Bitmap;
@@ -36,6 +39,7 @@
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -102,14 +106,14 @@
  * An accessibility is declared as any other service in an AndroidManifest.xml, but it
  * must do two things:
  * <ul>
- *     <ol>
+ *     <li>
  *         Specify that it handles the "android.accessibilityservice.AccessibilityService"
  *         {@link android.content.Intent}.
- *     </ol>
- *     <ol>
+ *     </li>
+ *     <li>
  *         Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
  *         ensure that only the system can bind to it.
- *     </ol>
+ *     </li>
  * </ul>
  * If either of these items is missing, the system will ignore the accessibility service.
  * Following is an example declaration:
@@ -961,30 +965,31 @@
         }
     }
 
+    @NonNull
     @Override
     public Context createDisplayContext(Display display) {
-        final Context context = super.createDisplayContext(display);
-        final int displayId = display.getDisplayId();
-        setDefaultTokenInternal(context, displayId);
-        return context;
+        return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
     }
 
-    private void setDefaultTokenInternal(Context context, int displayId) {
-        final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
-        final IAccessibilityServiceConnection connection =
-                AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
-        IBinder token = null;
-        if (connection != null) {
-            synchronized (mLock) {
-                try {
-                    token = connection.getOverlayWindowToken(displayId);
-                } catch (RemoteException re) {
-                    Log.w(LOG_TAG, "Failed to get window token", re);
-                    re.rethrowFromSystemServer();
-                }
-            }
-            wm.setDefaultToken(token);
+    @NonNull
+    @Override
+    public Context createWindowContext(int type, @Nullable Bundle options) {
+        final Context context = super.createWindowContext(type, options);
+        if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+            return context;
         }
+        return new AccessibilityContext(context, mConnectionId);
+    }
+
+    @NonNull
+    @Override
+    public Context createWindowContext(@NonNull Display display, int type,
+            @Nullable Bundle options) {
+        final Context context = super.createWindowContext(display, type, options);
+        if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+            return context;
+        }
+        return new AccessibilityContext(context, mConnectionId);
     }
 
     /**
@@ -2069,6 +2074,10 @@
         if (WINDOW_SERVICE.equals(name)) {
             if (mWindowManager == null) {
                 mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
+                final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+                // Set e default token obtained from the connection to ensure client could use
+                // accessibility overlay.
+                wm.setDefaultToken(mWindowToken);
             }
             return mWindowManager;
         }
@@ -2177,8 +2186,10 @@
 
                 // The client may have already obtained the window manager, so
                 // update the default token on whatever manager we gave them.
-                final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
-                wm.setDefaultToken(windowToken);
+                if (mWindowManager != null) {
+                    final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+                    wm.setDefaultToken(mWindowToken);
+                }
             }
 
             @Override
@@ -2400,6 +2411,14 @@
                     if (connection != null) {
                         AccessibilityInteractionClient.getInstance(mContext).addConnection(
                                 mConnectionId, connection);
+                        if (mContext != null) {
+                            try {
+                                connection.setAttributionTag(mContext.getAttributionTag());
+                            } catch (RemoteException re) {
+                                Log.w(LOG_TAG, "Error while setting attributionTag", re);
+                                re.rethrowFromSystemServer();
+                            }
+                        }
                         mCallback.init(mConnectionId, windowToken);
                         mCallback.onServiceConnected();
                     } else {
@@ -2675,4 +2694,58 @@
             }
         }
     }
+
+    private static class AccessibilityContext extends ContextWrapper {
+        private final int mConnectionId;
+
+        private AccessibilityContext(Context base, int connectionId) {
+            super(base);
+            mConnectionId = connectionId;
+            setDefaultTokenInternal(this, getDisplayId());
+        }
+
+        @NonNull
+        @Override
+        public Context createDisplayContext(Display display) {
+            return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
+        }
+
+        @NonNull
+        @Override
+        public Context createWindowContext(int type, @Nullable Bundle options) {
+            final Context context = super.createWindowContext(type, options);
+            if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+                return context;
+            }
+            return new AccessibilityContext(context, mConnectionId);
+        }
+
+        @NonNull
+        @Override
+        public Context createWindowContext(@NonNull Display display, int type,
+                @Nullable Bundle options) {
+            final Context context = super.createWindowContext(display, type, options);
+            if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+                return context;
+            }
+            return new AccessibilityContext(context, mConnectionId);
+        }
+
+        private void setDefaultTokenInternal(Context context, int displayId) {
+            final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
+                    WINDOW_SERVICE);
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getConnection(mConnectionId);
+            IBinder token = null;
+            if (connection != null) {
+                try {
+                    token = connection.getOverlayWindowToken(displayId);
+                } catch (RemoteException re) {
+                    Log.w(LOG_TAG, "Failed to get window token", re);
+                    re.rethrowFromSystemServer();
+                }
+                wm.setDefaultToken(token);
+            }
+        }
+    }
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java
new file mode 100644
index 0000000..f28015a
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityTrace.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.accessibilityservice;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface to log accessibility trace.
+ *
+ * @hide
+ */
+public interface AccessibilityTrace {
+    String NAME_ACCESSIBILITY_SERVICE_CONNECTION = "IAccessibilityServiceConnection";
+    String NAME_ACCESSIBILITY_SERVICE_CLIENT = "IAccessibilityServiceClient";
+    String NAME_ACCESSIBILITY_MANAGER = "IAccessibilityManager";
+    String NAME_ACCESSIBILITY_MANAGER_CLIENT = "IAccessibilityManagerClient";
+    String NAME_ACCESSIBILITY_INTERACTION_CONNECTION = "IAccessibilityInteractionConnection";
+    String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK =
+            "IAccessibilityInteractionConnectionCallback";
+    String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback";
+    String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection";
+    String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback";
+    String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal";
+    String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback";
+    String NAME_MAGNIFICATION_CALLBACK = "MagnificationCallbacks";
+    String NAME_INPUT_FILTER = "InputFilter";
+    String NAME_GESTURE = "Gesture";
+    String NAME_ACCESSIBILITY_SERVICE = "AccessibilityService";
+    String NAME_PACKAGE_BROADCAST_RECEIVER = "PMBroadcastReceiver";
+    String NAME_USER_BROADCAST_RECEIVER = "UserBroadcastReceiver";
+    String NAME_FINGERPRINT = "FingerprintGesture";
+    String NAME_ACCESSIBILITY_INTERACTION_CLIENT = "AccessibilityInteractionClient";
+
+    String NAME_ALL_LOGGINGS = "AllLoggings";
+    String NAME_NONE = "None";
+
+    long FLAGS_ACCESSIBILITY_SERVICE_CONNECTION = 0x0000000000000001L;
+    long FLAGS_ACCESSIBILITY_SERVICE_CLIENT = 0x0000000000000002L;
+    long FLAGS_ACCESSIBILITY_MANAGER = 0x0000000000000004L;
+    long FLAGS_ACCESSIBILITY_MANAGER_CLIENT = 0x0000000000000008L;
+    long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L;
+    long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L;
+    long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L;
+    long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
+    long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L;
+    long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L;
+    long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L;
+    long FLAGS_MAGNIFICATION_CALLBACK = 0x0000000000000800L;
+    long FLAGS_INPUT_FILTER = 0x0000000000001000L;
+    long FLAGS_GESTURE = 0x0000000000002000L;
+    long FLAGS_ACCESSIBILITY_SERVICE = 0x0000000000004000L;
+    long FLAGS_PACKAGE_BROADCAST_RECEIVER = 0x0000000000008000L;
+    long FLAGS_USER_BROADCAST_RECEIVER = 0x0000000000010000L;
+    long FLAGS_FINGERPRINT = 0x0000000000020000L;
+    long FLAGS_ACCESSIBILITY_INTERACTION_CLIENT = 0x0000000000040000L;
+
+    long FLAGS_LOGGING_NONE = 0x0000000000000000L;
+    long FLAGS_LOGGING_ALL = 0xFFFFFFFFFFFFFFFFL;
+
+    long FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES = FLAGS_ACCESSIBILITY_INTERACTION_CLIENT
+            | FLAGS_ACCESSIBILITY_SERVICE
+            | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION
+            | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
+    Map<String, Long> sNamesToFlags = Map.ofEntries(
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_SERVICE_CONNECTION, FLAGS_ACCESSIBILITY_SERVICE_CONNECTION),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_SERVICE_CLIENT, FLAGS_ACCESSIBILITY_SERVICE_CLIENT),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_MANAGER, FLAGS_ACCESSIBILITY_MANAGER),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_MANAGER_CLIENT, FLAGS_ACCESSIBILITY_MANAGER_CLIENT),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_INTERACTION_CONNECTION,
+                    FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK,
+                    FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK,
+                    FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_WINDOW_MANAGER_INTERNAL, FLAGS_WINDOW_MANAGER_INTERNAL),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_MAGNIFICATION_CALLBACK, FLAGS_MAGNIFICATION_CALLBACK),
+            new AbstractMap.SimpleEntry<String, Long>(NAME_INPUT_FILTER, FLAGS_INPUT_FILTER),
+            new AbstractMap.SimpleEntry<String, Long>(NAME_GESTURE, FLAGS_GESTURE),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_SERVICE, FLAGS_ACCESSIBILITY_SERVICE),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_PACKAGE_BROADCAST_RECEIVER, FLAGS_PACKAGE_BROADCAST_RECEIVER),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_USER_BROADCAST_RECEIVER, FLAGS_USER_BROADCAST_RECEIVER),
+            new AbstractMap.SimpleEntry<String, Long>(NAME_FINGERPRINT, FLAGS_FINGERPRINT),
+            new AbstractMap.SimpleEntry<String, Long>(
+                    NAME_ACCESSIBILITY_INTERACTION_CLIENT, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT),
+            new AbstractMap.SimpleEntry<String, Long>(NAME_NONE, FLAGS_LOGGING_NONE),
+            new AbstractMap.SimpleEntry<String, Long>(NAME_ALL_LOGGINGS, FLAGS_LOGGING_ALL));
+
+    /**
+     * Get the flags of the logging types by the given names.
+     * The names list contains logging type names in lower case.
+     */
+    static long getLoggingFlagsFromNames(List<String> names) {
+        long types = FLAGS_LOGGING_NONE;
+        for (String name : names) {
+            long flag = sNamesToFlags.get(name);
+            types |= flag;
+        }
+        return types;
+    }
+
+    /**
+     * Get the list of the names of logging types by the given flags.
+     */
+    static List<String> getNamesOfLoggingTypes(long flags) {
+        List<String> list = new ArrayList<String>();
+
+        for (Map.Entry<String, Long> entry : sNamesToFlags.entrySet()) {
+            if ((entry.getValue() & flags) != FLAGS_LOGGING_NONE) {
+                list.add(entry.getKey());
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Whether the trace is enabled for any logging type.
+     */
+    boolean isA11yTracingEnabled();
+
+    /**
+     * Whether the trace is enabled for any of the given logging type.
+     */
+    boolean isA11yTracingEnabledForTypes(long typeIdFlags);
+
+    /**
+     * Get trace state to be sent to AccessibilityManager.
+     */
+    int getTraceStateForAccessibilityManagerClientState();
+
+    /**
+     * Start tracing for the given logging types.
+     */
+    void startTrace(long flagss);
+
+    /**
+     * Stop tracing.
+     */
+    void stopTrace();
+
+    /**
+     * Log one trace entry.
+     * @param where A string to identify this log entry, which can be used to search through the
+     *        tracing file.
+     * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+     *        can be used to filter the log entries when generating tracing file.
+     */
+    void logTrace(String where, long loggingFlags);
+
+    /**
+     * Log one trace entry.
+     * @param where A string to identify this log entry, which can be used to filter/search
+     *        through the tracing file.
+     * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+     *        can be used to filter the log entries when generating tracing file.
+     * @param callingParams The parameters for the method to be logged.
+     */
+    void logTrace(String where, long loggingFlags, String callingParams);
+
+    /**
+     * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
+     * make screen content related requests use this API to log entry when receive callback.
+     * @param timestamp The timestamp when a callback is received.
+     * @param where A string to identify this log entry, which can be used to filter/search
+     *        through the tracing file.
+     * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+     *        can be used to filter the log entries when generating tracing file.
+     * @param callingParams The parameters for the callback.
+     * @param processId The process id of the calling component.
+     * @param threadId The threadId of the calling component.
+     * @param callingUid The calling uid of the callback.
+     * @param callStack The call stack of the callback.
+     * @param ignoreStackElements ignore these call stack element
+     */
+    void logTrace(long timestamp, String where, long loggingFlags, String callingParams,
+            int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+            Set<String> ignoreStackElements);
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 923b6f4..6c360e5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -39,6 +39,8 @@
 
     void setServiceInfo(in AccessibilityServiceInfo info);
 
+    void setAttributionTag(in String attributionTag);
+
     String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
         long accessibilityNodeId, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -118,6 +120,6 @@
 
     void setFocusAppearance(int strokeWidth, int color);
 
-    oneway void logTrace(long timestamp, String where, String callingParams, int processId,
-        long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+    oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+        int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
 }
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 7b57442f..3807b50 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -88,7 +88,8 @@
  * {@link AccountManager#KEY_INTENT}.
  * <p>
  * The activity needs to return the final result when it is complete so the Intent should contain
- * the {@link AccountAuthenticatorResponse} as {@link AccountManager#KEY_ACCOUNT_MANAGER_RESPONSE}.
+ * the {@link AccountAuthenticatorResponse} as
+ * {@link AccountManager#KEY_ACCOUNT_AUTHENTICATOR_RESPONSE}.
  * The activity must then call {@link AccountAuthenticatorResponse#onResult} or
  * {@link AccountAuthenticatorResponse#onError} when it is complete.
  * <li> If the authenticator cannot synchronously process the request and return a result then it
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 0d6a079..e6cdcc0 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -86,6 +87,12 @@
         if (TextUtils.isEmpty(type)) {
             throw new IllegalArgumentException("the type must not be empty: " + type);
         }
+        if (name.length() > 200) {
+            throw new IllegalArgumentException("account name is longer than 200 characters");
+        }
+        if (type.length() > 200) {
+            throw new IllegalArgumentException("account type is longer than 200 characters");
+        }
         this.name = name;
         this.type = type;
         this.accessId = accessId;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 79fb863..2bbf280 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -349,6 +350,7 @@
 
     private static final class UserIdPackage
     {
+        @UserIdInt
         public int userId;
         public String packageName;
 
@@ -379,7 +381,8 @@
     }
 
     PropertyInvalidatedCache<UserIdPackage, Account[]> mAccountsForUserCache =
-        new PropertyInvalidatedCache<UserIdPackage, Account[]>(CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
+                new PropertyInvalidatedCache<UserIdPackage, Account[]>(
+                CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
         @Override
         protected Account[] recompute(UserIdPackage userAndPackage) {
             try {
@@ -389,6 +392,10 @@
             }
         }
         @Override
+        protected boolean bypass(UserIdPackage query) {
+            return query.userId < 0;
+        }
+        @Override
         protected boolean debugCompareQueryResults(Account[] l, Account[] r) {
             if (l == r) {
                 return true;
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index 8dcc04a..6ad9d92 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -1,9 +1,4 @@
-carlosvaldivia@google.com
+jcivelli@google.com
 dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
 yamasani@google.com
 omakoto@google.com
-
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index bd43868..2efdf51 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -205,6 +206,18 @@
         }
     }
 
+    /**
+     * Returns the activity token below in the same task if it belongs to the same process.
+     */
+    @Nullable
+    public IBinder getActivityTokenBelow(IBinder activityToken) {
+        try {
+            return getActivityClientController().getActivityTokenBelow(activityToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     ComponentName getCallingActivity(IBinder token) {
         try {
             return getActivityClientController().getCallingActivity(token);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4376d22..b29349e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2835,7 +2835,13 @@
      * Returns a list of any processes that are currently in an error condition.  The result
      * will be null if all processes are running properly at this time.
      *
-     * @return Returns a list of ProcessErrorStateInfo records, or null if there are no
+     * <p>As of {@link android.os.Build.VERSION_CODES#TIRAMISU Android TIRAMISU}, for regular apps
+     * this method will only return {@link ProcessErrorStateInfo} records for the processes running
+     * as the caller's uid, unless the caller has the permission
+     * {@link android.Manifest.permission#DUMP}.
+     * </p>
+     *
+     * @return Returns a list of {@link ProcessErrorStateInfo} records, or null if there are no
      * current error conditions (it will not return an empty list).  This list ordering is not
      * specified.
      */
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0d68df4..7145c0f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -377,7 +377,7 @@
             IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
             @UserIdInt int userId, boolean allowBackgroundActivityStarts,
-            @Nullable IBinder backgroundActivityStartsToken);
+            @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList);
 
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage,
@@ -646,4 +646,9 @@
      */
     @Nullable
     public abstract List<Integer> getIsolatedProcesses(int uid);
+
+    /** @see ActivityManagerService#sendIntentSender */
+    public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
+            Intent intent, String resolvedType,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
 }
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4a7fcd2..a836625 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -476,6 +476,19 @@
     }
 
     /**
+     * Detaches the navigation bar from the app it was attached to during a transition.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+    public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+        try {
+            getService().detachNavigationBarFromApp(transition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Information you can retrieve about a root task in the system.
      * @hide
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3915abe..e3abe4d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
 import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
-import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
@@ -31,6 +30,10 @@
 import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
 import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
@@ -88,10 +91,8 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.HardwareRenderer;
-import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.hardware.display.DisplayManagerGlobal;
-import android.inputmethodservice.InputMethodService;
 import android.media.MediaFrameworkInitializer;
 import android.media.MediaFrameworkPlatformInitializer;
 import android.media.MediaServiceManager;
@@ -183,6 +184,7 @@
 import android.window.SizeConfigurationBuckets;
 import android.window.SplashScreen;
 import android.window.SplashScreenView;
+import android.window.WindowProviderService;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -3550,6 +3552,13 @@
                     + ", comp=" + r.intent.getComponent().toShortString()
                     + ", dir=" + r.packageInfo.getAppDir());
 
+            // updatePendingActivityConfiguration() reads from mActivities to update
+            // ActivityClientRecord which runs in a different thread. Protect modifications to
+            // mActivities to avoid race.
+            synchronized (mResourcesManager) {
+                mActivities.put(r.token, r);
+            }
+
             if (activity != null) {
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                 Configuration config =
@@ -3611,13 +3620,6 @@
             }
             r.setState(ON_CREATE);
 
-            // updatePendingActivityConfiguration() reads from mActivities to update
-            // ActivityClientRecord which runs in a different thread. Protect modifications to
-            // mActivities to avoid race.
-            synchronized (mResourcesManager) {
-                mActivities.put(r.token, r);
-            }
-
         } catch (SuperNotCalledException e) {
             throw e;
 
@@ -5428,6 +5430,12 @@
                     // behave properly when activity is relaunching.
                     r.window.clearContentView();
                 } else {
+                    final ViewRootImpl viewRoot = v.getViewRootImpl();
+                    if (viewRoot != null) {
+                        // Clear the callback to avoid the destroyed activity from receiving
+                        // configuration changes that are no longer effective.
+                        viewRoot.setActivityConfigCallback(null);
+                    }
                     wm.removeViewImmediate(v);
                 }
             }
@@ -5751,7 +5759,7 @@
     }
 
     @Override
-    public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
+    public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
         ArrayList<ComponentCallbacks2> callbacks
                 = new ArrayList<ComponentCallbacks2>();
 
@@ -5760,7 +5768,7 @@
             for (int i=0; i<NAPP; i++) {
                 callbacks.add(mAllApplications.get(i));
             }
-            if (includeActivities) {
+            if (includeUiContexts) {
                 for (int i = mActivities.size() - 1; i >= 0; i--) {
                     final Activity a = mActivities.valueAt(i).activity;
                     if (a != null && !a.mFinished) {
@@ -5770,11 +5778,12 @@
             }
             final int NSVC = mServices.size();
             for (int i=0; i<NSVC; i++) {
-                final ComponentCallbacks2 serviceComp = mServices.valueAt(i);
-                if (serviceComp instanceof InputMethodService) {
-                    mHasImeComponent = true;
+                final Service service = mServices.valueAt(i);
+                // If {@code includeUiContext} is set to false, WindowProviderService should not be
+                // collected because WindowProviderService is a UI Context.
+                if (includeUiContexts || !(service instanceof WindowProviderService)) {
+                    callbacks.add(service);
                 }
-                callbacks.add(serviceComp);
             }
         }
         synchronized (mProviderMap) {
@@ -5829,35 +5838,25 @@
         // change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
         handleWindowingModeChangeIfNeeded(activity, newConfig);
 
-        final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
-        boolean shouldReportChange = false;
-        if (activity.mCurrentConfig == null) {
-            shouldReportChange = true;
-        } else {
-            // If the new config is the same as the config this Activity is already running with and
-            // the override config also didn't change, then don't bother calling
-            // onConfigurationChanged.
-            // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
-            // ResourcesImpl constructions.
-            int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
-            final ActivityClientRecord cr = getActivityClient(activityToken);
-            diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig,
-                    cr != null ? cr.mSizeConfigurations : null);
-
-            if (diff == 0) {
-                if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
-                        && !movedToDifferentDisplay
-                        && mResourcesManager.isSameResourcesOverrideConfig(
-                                activityToken, amOverrideConfig)) {
-                    // Nothing significant, don't proceed with updating and reporting.
-                    return null;
-                }
-            } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
+        final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
+                displayId);
+        final ActivityClientRecord r = mActivities.get(activityToken);
+        final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig,
+                newConfig, r != null ? r.mSizeConfigurations : null);
+        final boolean hasPublicConfigChange = diff != 0;
+        // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
+        // ResourcesImpl constructions.
+        final boolean shouldUpdateResources = hasPublicConfigChange
+                || shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig,
+                amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange);
+        final boolean shouldReportChange = hasPublicConfigChange
                 // If this activity doesn't handle any of the config changes, then don't bother
                 // calling onConfigurationChanged. Otherwise, report to the activity for the
                 // changes.
-                shouldReportChange = true;
-            }
+                && (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0;
+        // Nothing significant, don't proceed with updating and reporting.
+        if (!shouldUpdateResources) {
+            return null;
         }
 
         // Propagate the configuration change to ResourcesManager and Activity.
@@ -5908,26 +5907,6 @@
         return configToReport;
     }
 
-    // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
-    // constructions.
-    /**
-     * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
-     * should be updated.
-     *
-     * @see WindowManager#getCurrentWindowMetrics()
-     * @see WindowManager#getMaximumWindowMetrics()
-     */
-    private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
-            @NonNull Configuration newConfig) {
-        final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
-        final Rect newBounds = newConfig.windowConfiguration.getBounds();
-
-        final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
-        final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
-
-        return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
-    }
-
     public final void applyConfigurationToResources(Configuration config) {
         synchronized (mResourcesManager) {
             mResourcesManager.applyConfigurationToResources(config, null);
@@ -6071,7 +6050,8 @@
             // display.
             displayId = r.activity.getDisplayId();
         }
-        final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
+        final boolean movedToDifferentDisplay = isDifferentDisplay(
+                r.activity.getDisplayId(), displayId);
         if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
                 && !movedToDifferentDisplay) {
             if (DEBUG_CONFIGURATION) {
@@ -6107,14 +6087,6 @@
         mSomeActivitiesChanged = true;
     }
 
-    /**
-     * Checks if the display id of activity is different from the given one. Note that
-     * {@link Display#INVALID_DISPLAY} means no difference.
-     */
-    private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
-        return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
-    }
-
     final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
         if (start) {
             try {
@@ -6294,7 +6266,7 @@
 
     final void handleLowMemory() {
         final ArrayList<ComponentCallbacks2> callbacks =
-                collectComponentCallbacks(true /* includeActivities */);
+                collectComponentCallbacks(true /* includeUiContexts */);
 
         final int N = callbacks.size();
         for (int i=0; i<N; i++) {
@@ -6327,7 +6299,7 @@
         }
 
         final ArrayList<ComponentCallbacks2> callbacks =
-                collectComponentCallbacks(true /* includeActivities */);
+                collectComponentCallbacks(true /* includeUiContexts */);
 
         final int N = callbacks.size();
         for (int i = 0; i < N; i++) {
@@ -7546,12 +7518,6 @@
 
         ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
             synchronized (mResourcesManager) {
-                // TODO (b/135719017): Temporary log for debugging IME service.
-                if (Build.IS_DEBUGGABLE && mHasImeComponent) {
-                    Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "
-                            + "config=" + globalConfig);
-                }
-
                 // We need to apply this change to the resources immediately, because upon returning
                 // the view hierarchy will be informed about it.
                 if (mResourcesManager.applyConfigurationToResources(globalConfig,
@@ -7906,11 +7872,6 @@
         return mDensityCompatMode;
     }
 
-    @Override
-    public boolean hasImeComponent() {
-        return mHasImeComponent;
-    }
-
     // ------------------ Regular JNI ------------------------
     private native void nPurgePendingResources();
     private native void nDumpGraphicsInfo(FileDescriptor fd);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index d91933c..bc698f6 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -32,11 +32,9 @@
 
     boolean isInDensityCompatMode();
 
-    boolean hasImeComponent();
-
     boolean isCachedProcessState();
 
     Application getApplication();
 
-    ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities);
+    ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd2c12c..245baf0 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -317,6 +317,16 @@
     }
 
     @Override
+    public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+        try {
+            return mPM.getLaunchIntentSenderForPackage(packageName, mContext.getPackageName(),
+                    mContext.getAttributionTag(), getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public int[] getPackageGids(String packageName) throws NameNotFoundException {
         return getPackageGids(packageName, 0);
     }
@@ -1004,13 +1014,17 @@
                     }
                 }
                 @Override
+                protected boolean bypass(Integer uid) {
+                    return uid < 0;
+                }
+                @Override
                 public String queryToString(Integer uid) {
                     return String.format("uid=%d", uid.intValue());
                 }
             };
 
     @Override
-    public String[] getPackagesForUid(int uid) {
+    public String[] getPackagesForUid(@UserIdInt int uid) {
         return mGetPackagesForUidCache.query(uid).value();
     }
 
@@ -1025,7 +1039,7 @@
     }
 
     @Override
-    public String getNameForUid(int uid) {
+    public String getNameForUid(@UserIdInt int uid) {
         try {
             return mPM.getNameForUid(uid);
         } catch (RemoteException e) {
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index f79e078..8637e31 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -17,24 +17,20 @@
 package android.app;
 
 import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.HardwareRenderer;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
 import android.os.LocaleList;
 import android.os.Trace;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.util.Slog;
 import android.view.ContextThemeWrapper;
 import android.view.WindowManagerGlobal;
@@ -169,12 +165,7 @@
                 mPendingConfiguration = null;
             }
 
-            final boolean hasIme = mActivityThread.hasImeComponent();
             if (config == null) {
-                // TODO (b/135719017): Temporary log for debugging IME service.
-                if (Build.IS_DEBUGGABLE && hasIme) {
-                    Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
-                }
                 return;
             }
 
@@ -205,12 +196,6 @@
                 mConfiguration = new Configuration();
             }
             if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
-                // TODO (b/135719017): Temporary log for debugging IME service.
-                if (Build.IS_DEBUGGABLE && hasIme) {
-                    Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
-                            + ", config=" + config
-                            + ", mConfiguration=" + mConfiguration);
-                }
                 return;
             }
 
@@ -228,7 +213,7 @@
         }
 
         final ArrayList<ComponentCallbacks2> callbacks =
-                mActivityThread.collectComponentCallbacks(false /* includeActivities */);
+                mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
 
         freeTextLayoutCachesIfNeeded(configDiff);
 
@@ -238,13 +223,6 @@
                 ComponentCallbacks2 cb = callbacks.get(i);
                 if (!equivalent) {
                     performConfigurationChanged(cb, config);
-                } else {
-                    // TODO (b/135719017): Temporary log for debugging IME service.
-                    if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
-                        Log.w(TAG, "performConfigurationChanged didn't callback to IME "
-                                + ", configDiff=" + configDiff
-                                + ", mConfiguration=" + mConfiguration);
-                    }
                 }
             }
         }
@@ -326,16 +304,4 @@
         return newConfig;
     }
 
-    /** Ask test layout engine to free its caches if there is a locale change. */
-    static void freeTextLayoutCachesIfNeeded(int configDiff) {
-        if (configDiff != 0) {
-            boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
-            if (hasLocaleConfigChange) {
-                Canvas.freeTextLayoutCaches();
-                if (DEBUG_CONFIGURATION) {
-                    Slog.v(TAG, "Cleared TextLayout Caches");
-                }
-            }
-        }
-    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f52fdc5..249a606 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2721,10 +2721,13 @@
         // need to override their display in ResourcesManager.
         baseContext.mForceDisplayOverrideInResources = false;
         baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
-        baseContext.mDisplay = display;
 
         final Resources windowContextResources = createWindowContextResources(baseContext);
         baseContext.setResources(windowContextResources);
+        // Associate the display with window context resources so that configuration update from
+        // the server side will also apply to the display's metrics.
+        baseContext.mDisplay = ResourcesManager.getInstance()
+                .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
 
         return baseContext;
     }
@@ -3187,12 +3190,6 @@
     @UnsupportedAppUsage
     final void setOuterContext(@NonNull Context context) {
         mOuterContext = context;
-        // TODO(b/149463653): check if we still need this method after migrating IMS to
-        //  WindowContext.
-        if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) {
-            mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
-            mIsConfigurationBasedContext = true;
-        }
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 5964f71..b324fb6 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -135,6 +135,7 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
     /**
      * Returns a list of supported game modes for a given package.
      * <p>
@@ -151,4 +152,19 @@
         }
     }
 
+    /**
+     * Returns if ANGLE is enabled for a given package and user ID.
+     * <p>
+     * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
+        try {
+            return mService.getAngleEnabled(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index c664969..aba6eb9 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -70,6 +70,7 @@
     boolean willActivityBeVisible(in IBinder token);
     int getDisplayId(in IBinder activityToken);
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
+    IBinder getActivityTokenBelow(IBinder token);
     ComponentName getCallingActivity(in IBinder token);
     String getCallingPackage(in IBinder token);
     int getLaunchedFromUid(in IBinder token);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 74d51a0..2be7803 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -330,4 +330,18 @@
      * When the Picture-in-picture state has changed.
      */
     void onPictureInPictureStateChanged(in PictureInPictureUiState pipState);
+
+    /**
+     * Re-attach navbar to the display during a recents transition.
+     * TODO(188595497): Remove this once navbar attachment is in shell.
+     */
+    void detachNavigationBarFromApp(in IBinder transition);
+
+    /**
+     * Marks a process as a delegate for the currently playing remote transition animation. This
+     * must be called from a process that is already a remote transition player or delegate. Any
+     * marked delegates are cleaned-up automatically at the end of the transition.
+     * @param caller is the IApplicationThread representing the calling process.
+     */
+    void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
 }
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 4bf8a3f..189f0a2 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -23,4 +23,5 @@
     int getGameMode(String packageName, int userId);
     void setGameMode(String packageName, int gameMode, int userId);
     int[] getAvailableGameModes(String packageName);
+    boolean getAngleEnabled(String packageName, int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9d149cf..ab88e6c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5539,12 +5539,11 @@
 
         /**
          * Determines if the notification should be colorized *for the purposes of applying colors*.
-         * If this is the minimized view of a colorized notification, or if the app did not provide
-         * a color to colorize with, this will return false so that internal coloring logic can
-         * still render the notification normally.
+         * If this is the minimized view of a colorized notification, this will return false so that
+         * internal coloring logic can still render the notification normally.
          */
         private boolean isBackgroundColorized(StandardTemplateParams p) {
-            return p.allowColorization && mN.color != COLOR_DEFAULT && mN.isColorized();
+            return p.allowColorization && mN.isColorized();
         }
 
         private boolean isCallActionColorCustomizable() {
@@ -5552,8 +5551,7 @@
             //  that is only used for disallowing colorization of headers for the minimized state,
             //  and neither of those conditions applies when showing actions.
             //  Not requiring StandardTemplateParams as an argument simplifies the creation process.
-            return mN.color != COLOR_DEFAULT && mN.isColorized()
-                    && mContext.getResources().getBoolean(
+            return mN.isColorized() && mContext.getResources().getBoolean(
                     R.bool.config_callNotificationActionColorsRequireColorized);
         }
 
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6ca7dfb..ef4d7b1 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -22,6 +22,7 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -66,12 +67,12 @@
  *
  * <pre>
  * public class UserBirthdayServiceImpl implements IUserBirthdayService {
- *   private final HashMap<Integer, Birthday> mUidToBirthday;
- *   @Override
+ *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ *   {@literal @}Override
  *   public synchronized Birthday getUserBirthday(int userId) {
  *     return mUidToBirthday.get(userId);
  *   }
- *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
  *     mUidToBirthday.clear();
  *     mUidToBirthday.putAll(uidToBirthday);
  *   }
@@ -105,9 +106,9 @@
  *   ...
  *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
  *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- *   private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
- *     PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- *       @Override
+ *   private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ *     PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ *       {@literal @}Override
  *       protected Birthday recompute(Integer userId) {
  *         return GetService("birthdayd").getUserBirthday(userId);
  *       }
@@ -140,7 +141,7 @@
  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
  *   }
  *
- *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
  *     mUidToBirthday.clear();
  *     mUidToBirthday.putAll(uidToBirthday);
  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
@@ -169,6 +170,41 @@
  * this local case, there's no IPC, so use of the cache is (depending on exact
  * circumstance) unnecessary.
  *
+ * There may be queries for which it is more efficient to bypass the cache than to cache
+ * the result.  This would be true, for example, if some queries would require frequent
+ * cache invalidation while other queries require infrequent invalidation.  To expand on
+ * the birthday example, suppose that there is a userId that signifies "the next
+ * birthday".  When passed this userId, the server returns the next birthday among all
+ * users - this value changes as time advances.  The userId value can be cached, but the
+ * cache must be invalidated whenever a birthday occurs, and this invalidates all
+ * birthdays.  If there is a large number of users, invalidation will happen so often that
+ * the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
+ *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ *   private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ *     PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ *       {@literal @}Override
+ *       protected Birthday recompute(Integer userId) {
+ *         return GetService("birthdayd").getUserBirthday(userId);
+ *       }
+ *       {@literal @}Override
+ *       protected boolean bypass(Integer userId) {
+ *         return userId == NEXT_BIRTHDAY;
+ *       }
+ *     };
+ *   ...
+ * }
+ * </pre>
+ *
+ * If the {@code bypass()} method returns true then the cache is not used for that
+ * particular query.  The {@code bypass()} method is not abstract and the default
+ * implementation returns false.
+ *
  * For security, there is a allowlist of processes that are allowed to invalidate a cache.
  * The allowlist includes normal runtime processes but does not include test processes.
  * Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
@@ -190,19 +226,23 @@
  */
 public abstract class PropertyInvalidatedCache<Query, Result> {
     /**
-     * Reserved nonce values.  The code is written assuming that these
-     * values are contiguous.
+     * Reserved nonce values.  Use isReservedNonce() to test for a reserved value.  Note
+     * that all values cause the cache to be skipped.
      */
     private static final int NONCE_UNSET = 0;
     private static final int NONCE_DISABLED = 1;
     private static final int NONCE_CORKED = 2;
-    private static final int NONCE_RESERVED = NONCE_CORKED + 1;
+    private static final int NONCE_BYPASS = 3;
+
+    private static boolean isReservedNonce(long n) {
+        return n >= NONCE_UNSET && n <= NONCE_BYPASS;
+    }
 
     /**
      * The names of the nonces
      */
     private static final String[] sNonceName =
-            new String[]{ "unset", "disabled", "corked" };
+            new String[]{ "unset", "disabled", "corked", "bypass" };
 
     private static final String TAG = "PropertyInvalidatedCache";
     private static final boolean DEBUG = false;
@@ -220,7 +260,7 @@
     private long mMisses = 0;
 
     @GuardedBy("mLock")
-    private long mSkips[] = new long[]{ 0, 0, 0 };
+    private long[] mSkips = new long[]{ 0, 0, 0, 0 };
 
     @GuardedBy("mLock")
     private long mMissOverflow = 0;
@@ -363,6 +403,91 @@
     }
 
     /**
+     * SystemProperties are protected and cannot be written (or read, usually) by random
+     * processes.  So, for testing purposes, the methods have a bypass mode that reads and
+     * writes to a HashMap and does not go out to the SystemProperties at all.
+     */
+
+    // If true, the cache might be under test.  If false, there is no testing in progress.
+    private static volatile boolean sTesting = false;
+
+    // If sTesting is true then keys that are under test are in this map.
+    private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>();
+
+    /**
+     * Enable or disable testing.  The testing property map is cleared every time this
+     * method is called.
+     */
+    @VisibleForTesting
+    public static void setTestMode(boolean mode) {
+        sTesting = mode;
+        synchronized (sTestingPropertyMap) {
+            sTestingPropertyMap.clear();
+        }
+    }
+
+  /**
+   * Enable testing the specific cache key.  Only keys in the map are subject to testing.
+   * There is no method to stop testing a property name.  Just disable the test mode.
+   */
+    @VisibleForTesting
+    public static void testPropertyName(String name) {
+        synchronized (sTestingPropertyMap) {
+            sTestingPropertyMap.put(name, (long) NONCE_UNSET);
+        }
+    }
+
+    // Read the system property associated with the current cache.  This method uses the
+    // handle for faster reading.
+    private long getCurrentNonce() {
+        if (sTesting) {
+            synchronized (sTestingPropertyMap) {
+                Long n = sTestingPropertyMap.get(mPropertyName);
+                if (n != null) {
+                    return n;
+                }
+            }
+        }
+
+        SystemProperties.Handle handle = mPropertyHandle;
+        if (handle == null) {
+            handle = SystemProperties.find(mPropertyName);
+            if (handle == null) {
+                return NONCE_UNSET;
+            }
+            mPropertyHandle = handle;
+        }
+        return handle.getLong(NONCE_UNSET);
+    }
+
+    // Write the nonce in a static context.  No handle is available.
+    private static void setNonce(String name, long val) {
+        if (sTesting) {
+            synchronized (sTestingPropertyMap) {
+                Long n = sTestingPropertyMap.get(name);
+                if (n != null) {
+                    sTestingPropertyMap.put(name, val);
+                    return;
+                }
+            }
+        }
+        SystemProperties.set(name, Long.toString(val));
+    }
+
+    // Set the nonce in a static context.  No handle is available.
+    private static long getNonce(String name) {
+        if (sTesting) {
+            synchronized (sTestingPropertyMap) {
+                Long n = sTestingPropertyMap.get(name);
+                if (n != null) {
+                    return n;
+                }
+            }
+        }
+        return SystemProperties.getLong(name, NONCE_UNSET);
+    }
+
+    /**
      * Forget all cached values.
      */
     public final void clear() {
@@ -418,18 +543,6 @@
         return oldResult;
     }
 
-    private long getCurrentNonce() {
-        SystemProperties.Handle handle = mPropertyHandle;
-        if (handle == null) {
-            handle = SystemProperties.find(mPropertyName);
-            if (handle == null) {
-                return NONCE_UNSET;
-            }
-            mPropertyHandle = handle;
-        }
-        return handle.getLong(NONCE_UNSET);
-    }
-
     /**
      * Disable the use of this cache in this process.
      */
@@ -477,9 +590,11 @@
     public Result query(Query query) {
         // Let access to mDisabled race: it's atomic anyway.
         long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
+        if (bypass(query)) {
+            currentNonce = NONCE_BYPASS;
+        }
         for (;;) {
-            if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET
-                    || currentNonce == NONCE_CORKED || bypass(query)) {
+            if (isReservedNonce(currentNonce)) {
                 if (!mDisabled) {
                     // Do not bother collecting statistics if the cache is
                     // locally disabled.
@@ -490,7 +605,7 @@
 
                 if (DEBUG) {
                     if (!mDisabled) {
-                        Log.d(TAG, String.format(
+                        Log.d(TAG, TextUtils.formatSimple(
                             "cache %s %s for %s",
                             cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
                     }
@@ -505,7 +620,7 @@
                     if (cachedResult != null) mHits++;
                 } else {
                     if (DEBUG) {
-                        Log.d(TAG, String.format(
+                        Log.d(TAG, TextUtils.formatSimple(
                             "clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
                             cacheName(), mCache.size(),
                             mLastSeenNonce, currentNonce));
@@ -531,9 +646,10 @@
                     if (currentNonce != afterRefreshNonce) {
                         currentNonce = afterRefreshNonce;
                         if (DEBUG) {
-                            Log.d(TAG, String.format("restarting %s %s because nonce changed in refresh",
-                                                     cacheName(),
-                                                     queryToString(query)));
+                            Log.d(TAG, TextUtils.formatSimple(
+                                    "restarting %s %s because nonce changed in refresh",
+                                    cacheName(),
+                                    queryToString(query)));
                         }
                         continue;
                     }
@@ -602,7 +718,7 @@
         if (!sEnabled) {
             return;
         }
-        SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+        setNonce(name, NONCE_DISABLED);
     }
 
     /**
@@ -622,7 +738,7 @@
     public static void invalidateCache(@NonNull String name) {
         if (!sEnabled) {
             if (DEBUG) {
-                Log.w(TAG, String.format(
+                Log.w(TAG, TextUtils.formatSimple(
                     "cache invalidate %s suppressed", name));
             }
             return;
@@ -651,7 +767,7 @@
     private static void invalidateCacheLocked(@NonNull String name) {
         // There's no race here: we don't require that values strictly increase, but instead
         // only that each is unique in a single runtime-restart session.
-        final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+        final long nonce = getNonce(name);
         if (nonce == NONCE_DISABLED) {
             if (DEBUG) {
                 Log.d(TAG, "refusing to invalidate disabled cache: " + name);
@@ -662,18 +778,15 @@
         long newValue;
         do {
             newValue = NoPreloadHolder.next();
-        } while (newValue >= 0 && newValue < NONCE_RESERVED);
-        final String newValueString = Long.toString(newValue);
+        } while (isReservedNonce(newValue));
         if (DEBUG) {
-            Log.d(TAG,
-                    String.format("invalidating cache [%s]: [%s] -> [%s]",
-                            name,
-                            nonce,
-                            newValueString));
+            Log.d(TAG, TextUtils.formatSimple(
+                    "invalidating cache [%s]: [%s] -> [%s]",
+                    name, nonce, Long.toString(newValue)));
         }
         // TODO(dancol): add an atomic compare and exchange property set operation to avoid a
         // small race with concurrent disable here.
-        SystemProperties.set(name, newValueString);
+        setNonce(name, newValue);
         long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
         sInvalidates.put(name, ++invalidateCount);
     }
@@ -693,7 +806,7 @@
     public static void corkInvalidations(@NonNull String name) {
         if (!sEnabled) {
             if (DEBUG) {
-                Log.w(TAG, String.format(
+                Log.w(TAG, TextUtils.formatSimple(
                     "cache cork %s suppressed", name));
             }
             return;
@@ -702,7 +815,8 @@
         synchronized (sCorkLock) {
             int numberCorks = sCorks.getOrDefault(name, 0);
             if (DEBUG) {
-                Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
+                Log.d(TAG, TextUtils.formatSimple(
+                        "corking %s: numberCorks=%s", name, numberCorks));
             }
 
             // If we're the first ones to cork this cache, set the cache to the corked state so
@@ -714,9 +828,9 @@
             // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
             // Implement this more dangerous mode of operation if necessary.
             if (numberCorks == 0) {
-                final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+                final long nonce = getNonce(name);
                 if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
-                    SystemProperties.set(name, Long.toString(NONCE_CORKED));
+                    setNonce(name, NONCE_CORKED);
                 }
             } else {
                 final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
@@ -739,8 +853,8 @@
     public static void uncorkInvalidations(@NonNull String name) {
         if (!sEnabled) {
             if (DEBUG) {
-                Log.w(TAG, String.format(
-                    "cache uncork %s suppressed", name));
+                Log.w(TAG, TextUtils.formatSimple(
+                        "cache uncork %s suppressed", name));
             }
             return;
         }
@@ -748,7 +862,8 @@
         synchronized (sCorkLock) {
             int numberCorks = sCorks.getOrDefault(name, 0);
             if (DEBUG) {
-                Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks));
+                Log.d(TAG, TextUtils.formatSimple(
+                        "uncorking %s: numberCorks=%s", name, numberCorks));
             }
 
             if (numberCorks < 1) {
@@ -816,7 +931,7 @@
             synchronized (mLock) {
                 boolean alreadyQueued = mUncorkDeadlineMs >= 0;
                 if (DEBUG) {
-                    Log.w(TAG, String.format(
+                    Log.w(TAG, TextUtils.formatSimple(
                             "autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
                             mUncorkDeadlineMs));
                 }
@@ -834,7 +949,7 @@
         private void handleMessage(Message msg) {
             synchronized (mLock) {
                 if (DEBUG) {
-                    Log.w(TAG, String.format(
+                    Log.w(TAG, TextUtils.formatSimple(
                             "handleMsesage %s mUncorkDeadlineMs=%s",
                             mPropertyName, mUncorkDeadlineMs));
                 }
@@ -846,7 +961,7 @@
                 if (mUncorkDeadlineMs > nowMs) {
                     mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
                     if (DEBUG) {
-                        Log.w(TAG, String.format(
+                        Log.w(TAG, TextUtils.formatSimple(
                                         "scheduling uncork at %s",
                                         mUncorkDeadlineMs));
                     }
@@ -880,10 +995,10 @@
             Result resultToCompare = recompute(query);
             boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
             if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
-                Log.e(TAG, String.format(
-                    "cache %s inconsistent for %s is %s should be %s",
-                    cacheName(), queryToString(query),
-                    proposedResult, resultToCompare));
+                Log.e(TAG, TextUtils.formatSimple(
+                        "cache %s inconsistent for %s is %s should be %s",
+                        cacheName(), queryToString(query),
+                        proposedResult, resultToCompare));
             }
             // Always return the "true" result in verification mode.
             return resultToCompare;
@@ -953,21 +1068,24 @@
         }
 
         synchronized (mLock) {
-            pw.println(String.format("  Cache Name: %s", cacheName()));
-            pw.println(String.format("    Property: %s", mPropertyName));
-            final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED];
-            pw.println(String.format("    Hits: %d, Misses: %d, Skips: %d, Clears: %d",
+            pw.println(TextUtils.formatSimple("  Cache Name: %s", cacheName()));
+            pw.println(TextUtils.formatSimple("    Property: %s", mPropertyName));
+            final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
+                    + mSkips[NONCE_BYPASS];
+            pw.println(TextUtils.formatSimple(
+                    "    Hits: %d, Misses: %d, Skips: %d, Clears: %d",
                     mHits, mMisses, skips, mClears));
-            pw.println(String.format("    Skip-corked: %d, Skip-unset: %d, Skip-other: %d",
+            pw.println(TextUtils.formatSimple(
+                    "    Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
                     mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
-                    mSkips[NONCE_DISABLED]));
-            pw.println(String.format(
+                    mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
+            pw.println(TextUtils.formatSimple(
                     "    Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
                     mLastSeenNonce, invalidateCount, corkedInvalidates));
-            pw.println(String.format(
+            pw.println(TextUtils.formatSimple(
                     "    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(TextUtils.formatSimple("    Enabled: %s", mDisabled ? "false" : "true"));
             pw.println("");
 
             Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
@@ -980,7 +1098,7 @@
                 String key = Objects.toString(entry.getKey());
                 String value = Objects.toString(entry.getValue());
 
-                pw.println(String.format("      Key: %s\n      Value: %s\n", key, value));
+                pw.println(TextUtils.formatSimple("      Key: %s\n      Value: %s\n", key, value));
             }
         }
     }
@@ -1009,7 +1127,7 @@
                     pw.println("  Corking Status:");
                     for (int i = 0; i < activeCorks.size(); i++) {
                         Map.Entry<String, Integer> entry = activeCorks.get(i);
-                        pw.println(String.format("    Property Name: %s Count: %d",
+                        pw.println(TextUtils.formatSimple("    Property Name: %s Count: %d",
                                 entry.getKey(), entry.getValue()));
                     }
                 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 198c33e..bf3778d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -693,7 +693,7 @@
      * @return true if activity resources override config matches the provided one or they are both
      *         null, false otherwise.
      */
-    boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
+    public boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
             @Nullable Configuration overrideConfig) {
         synchronized (mLock) {
             final ActivityResources activityResources
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 85758a9..cac7639 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -119,6 +120,12 @@
     public int displayId;
 
     /**
+     * The feature id of {@link com.android.server.wm.TaskDisplayArea} this task is associated with.
+     * @hide
+     */
+    public int displayAreaFeatureId = FEATURE_UNDEFINED;
+
+    /**
      * The recent activity values for the highest activity in the stack to have set the values.
      * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
      */
@@ -243,6 +250,12 @@
      */
     public boolean isVisible;
 
+    /**
+     * Whether this task is sleeping due to sleeping display.
+     * @hide
+     */
+    public boolean isSleeping;
+
     TaskInfo() {
         // Do nothing
     }
@@ -329,11 +342,10 @@
     }
 
     /**
-      * Returns {@code true} if parameters that are important for task organizers have changed
-      * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners
-      * about that.
-      * @hide
-      */
+     * Returns {@code true} if the parameters that are important for task organizers are equal
+     * between this {@link TaskInfo} and {@param that}.
+     * @hide
+     */
     public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) {
         if (that == null) {
             return false;
@@ -341,13 +353,15 @@
         return topActivityType == that.topActivityType
                 && isResizeable == that.isResizeable
                 && supportsMultiWindow == that.supportsMultiWindow
+                && displayAreaFeatureId == that.displayAreaFeatureId
                 && Objects.equals(positionInParent, that.positionInParent)
                 && Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
                 && Objects.equals(displayCutoutInsets, that.displayCutoutInsets)
                 && getWindowingMode() == that.getWindowingMode()
                 && Objects.equals(taskDescription, that.taskDescription)
                 && isFocused == that.isFocused
-                && isVisible == that.isVisible;
+                && isVisible == that.isVisible
+                && isSleeping == that.isSleeping;
     }
 
     /**
@@ -402,8 +416,10 @@
         parentTaskId = source.readInt();
         isFocused = source.readBoolean();
         isVisible = source.readBoolean();
+        isSleeping = source.readBoolean();
         topActivityInSizeCompat = source.readBoolean();
         mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
+        displayAreaFeatureId = source.readInt();
     }
 
     /**
@@ -440,8 +456,10 @@
         dest.writeInt(parentTaskId);
         dest.writeBoolean(isFocused);
         dest.writeBoolean(isVisible);
+        dest.writeBoolean(isSleeping);
         dest.writeBoolean(topActivityInSizeCompat);
         dest.writeTypedObject(mTopActivityLocusId, flags);
+        dest.writeInt(displayAreaFeatureId);
     }
 
     @Override
@@ -468,8 +486,10 @@
                 + " parentTaskId=" + parentTaskId
                 + " isFocused=" + isFocused
                 + " isVisible=" + isVisible
+                + " isSleeping=" + isSleeping
                 + " topActivityInSizeCompat=" + topActivityInSizeCompat
-                + " locusId= " + mTopActivityLocusId
+                + " locusId=" + mTopActivityLocusId
+                + " displayAreaFeatureId=" + displayAreaFeatureId
                 + "}";
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 549ab6e..5eb58bb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,6 +56,7 @@
 import android.net.PrivateDnsConnectivityChecker;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.nfc.NfcAdapter;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -332,8 +333,12 @@
      * {@link android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE} to be launched.
      * Only one {@link ComponentName} in the entire system should be enabled, and the rest of the
      * components are not started by this intent.
+     *
+     * @deprecated Starting from Android T, the system no longer launches an intent with this action
+     * when user provisioning completes.
      * @hide
      */
+    @Deprecated
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_STATE_USER_SETUP_COMPLETE =
@@ -1216,7 +1221,8 @@
             PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
             PROVISIONING_TRIGGER_QR_CODE,
             PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
-            PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+            PROVISIONING_TRIGGER_MANAGED_ACCOUNT,
+            PROVISIONING_TRIGGER_NFC
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProvisioningTrigger {}
@@ -1254,6 +1260,7 @@
      * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
      * @see #PROVISIONING_TRIGGER_QR_CODE
      * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+     * @see #PROVISIONING_TRIGGER_NFC
      * @hide
      */
     @SystemApi
@@ -1265,6 +1272,7 @@
      * @see #PROVISIONING_TRIGGER_QR_CODE
      * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
      * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @see #PROVISIONING_TRIGGER_NFC
      * @hide
      */
     @SystemApi
@@ -1276,6 +1284,7 @@
      * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
      * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
      * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @see #PROVISIONING_TRIGGER_NFC
      * @hide
      */
     @SystemApi
@@ -1295,6 +1304,7 @@
      * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
      * @see #PROVISIONING_TRIGGER_QR_CODE
      * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @see #PROVISIONING_TRIGGER_NFC
      * @hide
      */
     @SystemApi
@@ -1308,12 +1318,25 @@
      * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
      * @see #PROVISIONING_TRIGGER_QR_CODE
      * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @see #PROVISIONING_TRIGGER_NFC
      * @hide
      */
     @SystemApi
     public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4;
 
     /**
+     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning is
+     * triggered by tapping an NFC tag.
+     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+     * @see #PROVISIONING_TRIGGER_QR_CODE
+     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_TRIGGER_NFC = 5;
+
+    /**
      * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
      * organization-owned.
      *
@@ -5690,8 +5713,10 @@
 
     /**
      * Disable text entry into notifications on secure keyguard screens (e.g. PIN/Pattern/Password).
-     * This flag has no effect starting from version {@link android.os.Build.VERSION_CODES#N}
+     * @deprecated This flag was added in version {@link android.os.Build.VERSION_CODES#N}, but it
+     * never had any effect.
      */
+    @Deprecated
     public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 1 << 6;
 
     /**
@@ -14007,4 +14032,33 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Creates a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent
+     * from the provided {@code nfcIntent}.
+     *
+     * <p>Prerequisites to create the provisioning intent:
+     *
+     * <ul>
+     * <li>{@code nfcIntent}'s action is {@link NfcAdapter#ACTION_NDEF_DISCOVERED}</li>
+     * <li>{@code nfcIntent}'s NFC properties contain either
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} or
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} </li>
+     * </ul>
+     *
+     * This method returns {@code null} if the prerequisites are not met or if an error occurs
+     * when reading the NFC properties.
+     *
+     * @param nfcIntent the nfc intent generated from scanning a NFC tag
+     * @return a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent with
+     * intent extras as read by {@code nfcIntent}'s NFC properties or {@code null} if the
+     * prerequisites are not met or if an error occurs when reading the NFC properties.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
+        return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent);
+    }
 }
diff --git a/core/java/android/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java
new file mode 100644
index 0000000..fbad90c
--- /dev/null
+++ b/core/java/android/app/admin/ProvisioningIntentHelper.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.admin;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
+import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC;
+import static android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Utility class that provides functionality to create provisioning intents from nfc intents.
+ */
+final class ProvisioningIntentHelper {
+
+    private static final String TAG = "ProvisioningIntentHelper";
+
+    /**
+     * This class is never instantiated
+     */
+    private ProvisioningIntentHelper() { }
+
+    @Nullable
+    public static Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
+        requireNonNull(nfcIntent);
+
+        if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(nfcIntent.getAction())) {
+            Log.e(TAG, "Wrong Nfc action: " + nfcIntent.getAction());
+            return null;
+        }
+
+        NdefRecord firstRecord = getFirstNdefRecord(nfcIntent);
+
+        if (firstRecord != null) {
+            return createProvisioningIntentFromNdefRecord(firstRecord);
+        }
+
+        return null;
+    }
+
+
+    private static Intent createProvisioningIntentFromNdefRecord(NdefRecord firstRecord) {
+        requireNonNull(firstRecord);
+
+        Properties properties = loadPropertiesFromPayload(firstRecord.getPayload());
+
+        if (properties == null) {
+            Log.e(TAG, "Failed to load NdefRecord properties.");
+            return null;
+        }
+
+        Bundle bundle = createBundleFromProperties(properties);
+
+        if (!containsRequiredProvisioningExtras(bundle)) {
+            Log.e(TAG, "Bundle does not contain the required provisioning extras.");
+            return null;
+        }
+
+        return createProvisioningIntentFromBundle(bundle);
+    }
+
+    private static Properties loadPropertiesFromPayload(byte[] payload) {
+        Properties properties = new Properties();
+
+        try {
+            properties.load(new StringReader(new String(payload, UTF_8)));
+        } catch (IOException e) {
+            Log.e(TAG, "NFC Intent properties loading failed.");
+            return null;
+        }
+
+        return properties;
+    }
+
+    private static Bundle createBundleFromProperties(Properties properties) {
+        Enumeration propertyNames = properties.propertyNames();
+        Bundle bundle = new Bundle();
+
+        while (propertyNames.hasMoreElements()) {
+            String propertyName = (String) propertyNames.nextElement();
+            addPropertyToBundle(propertyName, properties, bundle);
+        }
+        return bundle;
+    }
+
+    private static void addPropertyToBundle(
+            String propertyName, Properties properties, Bundle bundle) {
+        if(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME.equals(propertyName)) {
+            ComponentName componentName = ComponentName.unflattenFromString(
+                    properties.getProperty(propertyName));
+            bundle.putParcelable(propertyName, componentName);
+        }
+        else {
+            bundle.putString(propertyName, properties.getProperty(propertyName));
+        }
+    }
+
+    private static Intent createProvisioningIntentFromBundle(Bundle bundle) {
+        requireNonNull(bundle);
+
+        Intent provisioningIntent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE);
+
+        provisioningIntent.putExtras(bundle);
+
+        provisioningIntent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_NFC);
+
+        return provisioningIntent;
+    }
+
+    private static boolean containsRequiredProvisioningExtras(Bundle bundle) {
+        return bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME) ||
+                bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
+    }
+
+    /**
+     * Returns the first {@link NdefRecord} found with a recognized MIME-type
+     */
+    private static NdefRecord getFirstNdefRecord(Intent nfcIntent) {
+        Parcelable[] ndefMessages = nfcIntent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES);
+        if (ndefMessages == null) {
+            Log.i(TAG, "No EXTRA_NDEF_MESSAGES from nfcIntent");
+            return null;
+        }
+
+        for (Parcelable rawMsg : ndefMessages) {
+            NdefMessage msg = (NdefMessage) rawMsg;
+            for (NdefRecord record : msg.getRecords()) {
+                String mimeType = new String(record.getType(), UTF_8);
+
+                // Only one first message with NFC_MIME_TYPE is used.
+                if (MIME_TYPE_PROVISIONING_NFC.equals(mimeType)) {
+                    return record;
+                }
+
+                // Assume only first record of message is used.
+                break;
+            }
+        }
+
+        Log.i(TAG, "No compatible records found on nfcIntent");
+        return null;
+    }
+}
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index fad6cd3..ebc2945 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * An app compat override applied to a given package and change id pairing.
@@ -139,6 +140,22 @@
 
     /** @hide */
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PackageOverride that = (PackageOverride) o;
+        return mMinVersionCode == that.mMinVersionCode && mMaxVersionCode == that.mMaxVersionCode
+                && mEnabled == that.mEnabled;
+    }
+
+    /** @hide */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMinVersionCode, mMaxVersionCode, mEnabled);
+    }
+
+    /** @hide */
+    @Override
     public String toString() {
         if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
             return Boolean.toString(mEnabled);
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index a33d0ad..9668f80 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -3,3 +3,5 @@
 yamasani@google.com
 mwachens@google.com
 varunshah@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8398be1..2ed26a9f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3059,6 +3059,9 @@
         } else if (profile == BluetoothProfile.LE_AUDIO) {
             BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
             return true;
+        } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
+            BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
+            return true;
         } else {
             return false;
         }
@@ -3151,6 +3154,11 @@
             case BluetoothProfile.LE_AUDIO:
                 BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
                 leAudio.close();
+                break;
+            case BluetoothProfile.VOLUME_CONTROL:
+                BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
+                vcs.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index aea8210..17a3e48 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -82,6 +82,9 @@
     private static final int CONN_STATE_DISCONNECTING = 3;
     private static final int CONN_STATE_CLOSED = 4;
 
+    private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
+    private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds
+
     private List<BluetoothGattService> mServices;
 
     /** A GATT operation completed successfully */
@@ -134,6 +137,27 @@
     public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
 
     /**
+     * A GATT writeCharacteristic request is started successfully.
+     *
+     * @hide
+     */
+    public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
+
+    /**
+     * A GATT writeCharacteristic request failed to start.
+     *
+     * @hide
+     */
+    public static final int GATT_WRITE_REQUEST_FAIL = 1;
+
+    /**
+     * A GATT writeCharacteristic request is issued to a busy remote device.
+     *
+     * @hide
+     */
+    public static final int GATT_WRITE_REQUEST_BUSY = 2;
+
+    /**
      * No authentication required.
      *
      * @hide
@@ -440,9 +464,19 @@
                         try {
                             final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
                                     ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.writeCharacteristic(mClientIf, address, handle,
-                                    characteristic.getWriteType(), authReq,
-                                    characteristic.getValue(), mAttributionSource);
+                            int requestStatus = GATT_WRITE_REQUEST_FAIL;
+                            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+                                requestStatus =  mService.writeCharacteristic(mClientIf, address, handle,
+                                                  characteristic.getWriteType(), authReq,
+                                                  characteristic.getValue(), mAttributionSource);
+                                if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+                                    break;
+                                }
+                                try {
+                                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+                                } catch (InterruptedException e) {
+                                }
+                            }
                             mAuthRetryState++;
                             return;
                         } catch (RemoteException e) {
@@ -1190,7 +1224,9 @@
                     characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
@@ -1226,7 +1262,9 @@
                     mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
@@ -1264,21 +1302,35 @@
         if (device == null) return false;
 
         synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
+            if (mDeviceBusy) {
+                return false;
+            }
             mDeviceBusy = true;
         }
 
+        int requestStatus = GATT_WRITE_REQUEST_FAIL;
         try {
-            mService.writeCharacteristic(mClientIf, device.getAddress(),
+            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+                requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
                     characteristic.getInstanceId(), characteristic.getWriteType(),
                     AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
+                if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+                    break;
+                }
+                try {
+                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+                } catch (InterruptedException e) {
+                }
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
-        return true;
+        return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
     }
 
     /**
@@ -1317,7 +1369,9 @@
                     descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
@@ -1359,7 +1413,9 @@
                     AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
@@ -1428,7 +1484,9 @@
             mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
-            mDeviceBusy = false;
+            synchronized (mDeviceBusyLock) {
+                mDeviceBusy = false;
+            }
             return false;
         }
 
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 9de27ff..c438dd3 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -356,7 +356,6 @@
      * earbud)
      * @param device LE Audio capable device
      * @return group id that this device currently belongs to
-     * @hide
      */
     @RequiresLegacyBluetoothPermission
     @RequiresBluetoothConnectPermission
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 161c843..83a272fc 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -208,17 +208,24 @@
     /**
      * LE Audio Device
      *
-     * @hide
      */
     int LE_AUDIO = 22;
 
     /**
+     * Volume Control profile
+     *
+     * @hide
+     */
+    @SystemApi
+    int VOLUME_CONTROL = 23;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    int MAX_PROFILE_ID = 22;
+    int MAX_PROFILE_ID = 23;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
new file mode 100644
index 0000000..678c11a
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Volume Control service.
+ *
+ * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothVolumeControl proxy object.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
+    private static final String TAG = "BluetoothVolumeControl";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    private CloseGuard mCloseGuard;
+
+    /**
+     * Intent used to broadcast the change in connection state of the Volume Control
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+
+    private BluetoothAdapter mAdapter;
+    private final AttributionSource mAttributionSource;
+    private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
+            new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
+                    IBluetoothVolumeControl.class.getName()) {
+                @Override
+                public IBluetoothVolumeControl getServiceInterface(IBinder service) {
+                    return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+                }
+            };
+
+    /**
+     * Create a BluetoothVolumeControl proxy object for interacting with the local
+     * Bluetooth Volume Control service.
+     */
+    /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
+            BluetoothAdapter adapter) {
+        mAdapter = adapter;
+        mAttributionSource = adapter.getAttributionSource();
+        mProfileConnector.connect(context, listener);
+        mCloseGuard = new CloseGuard();
+        mCloseGuard.open("close");
+    }
+
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    protected void finalize() {
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
+        }
+        close();
+    }
+
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void close() {
+        mProfileConnector.disconnect();
+    }
+
+    private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
+
+    /**
+     * Get the list of connected devices. Currently at most one.
+     *
+     * @return list of connected devices
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        final IBluetoothVolumeControl service = getService();
+        if (service != null && isEnabled()) {
+            try {
+                return Attributable.setAttributionSource(
+                        service.getConnectedDevices(mAttributionSource), mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get the list of devices matching specified states. Currently at most one.
+     *
+     * @return list of matching devices
+     *
+     * @hide
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        final IBluetoothVolumeControl service = getService();
+        if (service != null && isEnabled()) {
+            try {
+                return Attributable.setAttributionSource(
+                        service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+                        mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get connection state of device
+     *
+     * @return device connection state
+     *
+     * @hide
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        final IBluetoothVolumeControl service = getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.getConnectionState(device, mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Tells remote device to set an absolute volume.
+     *
+     * @param volume Absolute volume to be set on remote device.
+     *               Minimum value is 0 and maximum value is 255
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public void setVolume(@Nullable BluetoothDevice device,
+            @IntRange(from = 0, to = 255) int volume) {
+        if (DBG)
+            log("setVolume(" + volume + ")");
+        final IBluetoothVolumeControl service = getService();
+        try {
+            if (service != null && isEnabled()) {
+                service.setVolume(device, volume, mAttributionSource);
+                return;
+            }
+            if (service == null)
+                Log.w(TAG, "Proxy not attached to service");
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+        }
+    }
+
+    /**
+     * Set connection policy of the profile
+     *
+     * <p> The device should already be paired.
+     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Paired bluetooth device
+     * @param connectionPolicy is the connection policy to set to for this profile
+     * @return true if connectionPolicy is set, false on error
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
+        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        final IBluetoothVolumeControl service = getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+                    && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                return false;
+            }
+            try {
+                return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the connection policy of the profile.
+     *
+     * <p> The connection policy can be any of:
+     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+     * {@link #CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Bluetooth device
+     * @return connection policy of the device
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
+        if (VDBG) log("getConnectionPolicy(" + device + ")");
+        final IBluetoothVolumeControl service = getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.getConnectionPolicy(device, mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+    }
+
+    private boolean isEnabled() {
+        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+    }
+
+    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 4e5ede7..bb0b956 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -937,17 +937,18 @@
     }
 
     @NonNull
-    private String toHexString(@NonNull int b) {
+    private String toHexString(int b) {
         return toHexString(new byte[] {(byte) b});
     }
 
     @NonNull
-    private String toHexString(@NonNull byte b) {
+    private String toHexString(byte b) {
         return toHexString(new byte[] {b});
     }
 
     @NonNull
-    private String toHexString(@NonNull byte[] array) {
+    private String toHexString(byte[] array) {
+        if (array == null) return "null";
         StringBuilder builder = new StringBuilder(array.length * 2);
         for (byte b: array) {
             builder.append(String.format("%02x", b));
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 34aac8b..ee173db 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -151,7 +151,7 @@
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
     public void startScan(List<ScanFilter> filters, ScanSettings settings,
             final ScanCallback callback) {
-        startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
+        startScan(filters, settings, null, callback, /*callbackIntent=*/ null);
     }
 
     /**
@@ -185,7 +185,7 @@
             @NonNull PendingIntent callbackIntent) {
         return startScan(filters,
                 settings != null ? settings : new ScanSettings.Builder().build(),
-                null, null, callbackIntent, null);
+                null, null, callbackIntent);
     }
 
     /**
@@ -231,14 +231,13 @@
     @SuppressLint("AndroidFrameworkRequiresPermission")
     public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
             final WorkSource workSource, final ScanCallback callback) {
-        startScan(filters, settings, workSource, callback, null, null);
+        startScan(filters, settings, workSource, callback, null);
     }
 
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
     private int startScan(List<ScanFilter> filters, ScanSettings settings,
             final WorkSource workSource, final ScanCallback callback,
-            final PendingIntent callbackIntent,
-            List<List<ResultStorageDescriptor>> resultStorages) {
+            final PendingIntent callbackIntent) {
         BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
         if (callback == null && callbackIntent == null) {
             throw new IllegalArgumentException("callback is null");
@@ -274,7 +273,7 @@
             }
             if (callback != null) {
                 BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
-                        settings, workSource, callback, resultStorages);
+                        settings, workSource, callback);
                 wrapper.startRegistration();
             } else {
                 try {
@@ -357,8 +356,11 @@
     /**
      * Start truncated scan.
      *
+     * @deprecated this is not used anywhere
+     *
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresBluetoothScanPermission
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@@ -366,13 +368,10 @@
             final ScanCallback callback) {
         int filterSize = truncatedFilters.size();
         List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
-        List<List<ResultStorageDescriptor>> scanStorages =
-                new ArrayList<List<ResultStorageDescriptor>>(filterSize);
         for (TruncatedFilter filter : truncatedFilters) {
             scanFilters.add(filter.getFilter());
-            scanStorages.add(filter.getStorageDescriptors());
         }
-        startScan(scanFilters, settings, null, callback, null, scanStorages);
+        startScan(scanFilters, settings, null, callback, null);
     }
 
     /**
@@ -397,7 +396,6 @@
         private final WorkSource mWorkSource;
         private ScanSettings mSettings;
         private IBluetoothGatt mBluetoothGatt;
-        private List<List<ResultStorageDescriptor>> mResultStorages;
 
         // mLeHandle 0: not registered
         // -2: registration failed because app is scanning to frequently
@@ -407,15 +405,13 @@
 
         public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
                 List<ScanFilter> filters, ScanSettings settings,
-                WorkSource workSource, ScanCallback scanCallback,
-                List<List<ResultStorageDescriptor>> resultStorages) {
+                WorkSource workSource, ScanCallback scanCallback) {
             mBluetoothGatt = bluetoothGatt;
             mFilters = filters;
             mSettings = settings;
             mWorkSource = workSource;
             mScanCallback = scanCallback;
             mScannerId = 0;
-            mResultStorages = resultStorages;
         }
 
         public void startRegistration() {
@@ -493,7 +489,7 @@
                         } else {
                             mScannerId = scannerId;
                             mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
-                                    mResultStorages, mAttributionSource);
+                                    mAttributionSource);
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "fail to start le scan: " + e);
diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
index 796c815..f650489 100644
--- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java
+++ b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
@@ -23,8 +23,11 @@
 /**
  * Describes the way to store scan result.
  *
+ * @deprecated this is not used anywhere
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public final class ResultStorageDescriptor implements Parcelable {
     private int mType;
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
index 93f526b..2592588 100644
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ b/core/java/android/bluetooth/le/TruncatedFilter.java
@@ -24,8 +24,11 @@
 /**
  * A special scan filter that lets the client decide how the scan record should be stored.
  *
+ * @deprecated this is not used anywhere
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 @SuppressLint("AndroidFrameworkBluetoothPermission")
 public final class TruncatedFilter {
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 9007d9d..b060ce2 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -53,8 +53,6 @@
 
 
 
-
-
     // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
@@ -242,7 +240,7 @@
     };
 
     @DataClass.Generated(
-            time = 1612832377589L,
+            time = 1611795283642L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/Association.java",
             inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mNotifyOnDeviceNearby\nprivate final  long mTimeApprovedMs\npublic  int getUserId()\nprivate  java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 265ff33..563b6cf 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3193,6 +3193,21 @@
     }
 
     /**
+     * Returns the package name of the syncadapter that matches a given account type, authority
+     * and user.
+     * @hide
+     */
+    @Nullable
+    public static String getSyncAdapterPackageAsUser(@NonNull String accountType,
+            @NonNull String authority, @UserIdInt int userId) {
+        try {
+            return getContentService().getSyncAdapterPackageAsUser(accountType, authority, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if the provider should be synced when a network tickle is received
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
@@ -3289,7 +3304,7 @@
      * @param authority the provider to specify in the sync request
      * @param extras extra parameters to go along with the sync request
      * @param pollFrequency how frequently the sync should be performed, in seconds.
-     * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+     * On Android API level 24 and above, a minimum interval of 15 minutes is enforced.
      * On previous versions, the minimum interval is 1 hour.
      * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
      * are null.
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 2044fc0..127466d 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -150,6 +150,7 @@
     SyncAdapterType[] getSyncAdapterTypesAsUser(int userId);
 
     String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId);
+    String getSyncAdapterPackageAsUser(String accountType, String authority, int userId);
 
     /**
      * Returns true if there is currently a operation for the given account/authority or service
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32..e109ae5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1805,7 +1805,7 @@
      * the package name of the current installed package to be uninstalled.
      * You can optionally supply {@link #EXTRA_RETURN_RESULT}.
      * <p>
-     * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+     * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the uninstall
      * succeeded.
      * <p>
      * Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
@@ -5033,7 +5033,7 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
     /**
-     * An activity to run when device is inserted into a car dock.
+     * An activity to run when device is inserted into a desk dock.
      * Used with {@link #ACTION_MAIN} to launch an activity.  For more
      * information, see {@link android.app.UiModeManager}.
      */
@@ -5253,6 +5253,30 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
 
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the weather application.
+     * The activity should be able to give the user information about the weather
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
+
+    /**
+     * Used with {@link #ACTION_MAIN} to launch the fitness application.
+     * The activity should be able to give the user fitness information and manage workouts
+     * <p>NOTE: This should not be used as the primary key of an Intent,
+     * since it will not result in the app launching with the correct
+     * action and category.  Instead, use this with
+     * {@link #makeMainSelectorActivity(String, String)} to generate a main
+     * Intent with this category in the selector.</p>
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard extra data keys.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index c5badb9..32827ae 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -44,8 +45,10 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.function.Predicate;
 
 /**
  * Structured description of Intent values to be matched.  An IntentFilter can
@@ -147,6 +150,8 @@
  * will only match an Intent that does not have any categories.
  */
 public class IntentFilter implements Parcelable {
+    private static final String TAG = "IntentFilter";
+
     private static final String AGLOB_STR = "aglob";
     private static final String SGLOB_STR = "sglob";
     private static final String PREFIX_STR = "prefix";
@@ -1761,6 +1766,35 @@
     }
 
     /**
+     * Return a {@link Predicate} which tests whether this filter matches the
+     * given <var>intent</var>.
+     * <p>
+     * The intent's type will always be tested using a simple
+     * {@link Intent#getType()} check. To instead perform a detailed type
+     * resolution before matching, use
+     * {@link #asPredicateWithTypeResolution(ContentResolver)}.
+     */
+    public @NonNull Predicate<Intent> asPredicate() {
+        return i -> match(null, i, false, TAG) >= 0;
+    }
+
+    /**
+     * Return a {@link Predicate} which tests whether this filter matches the
+     * given <var>intent</var>.
+     * <p>
+     * The intent's type will always be resolved by calling
+     * {@link Intent#resolveType(ContentResolver)} before matching.
+     *
+     * @param resolver to be used when calling
+     *            {@link Intent#resolveType(ContentResolver)} before matching.
+     */
+    public @NonNull Predicate<Intent> asPredicateWithTypeResolution(
+            @NonNull ContentResolver resolver) {
+        Objects.requireNonNull(resolver);
+        return i -> match(resolver, i, true, TAG) >= 0;
+    }
+
+    /**
      * Test whether this filter matches the given <var>intent</var>.
      *
      * @param intent The Intent to compare against.
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index a167cb3..8d3452e 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -597,7 +597,7 @@
      * which 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>This API assumes the the {@link Binder#getCallingUid()} is the same as
+     * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
      * {@link Process#myUid()}.
      *
      * @param context Context for accessing resources.
@@ -634,7 +634,7 @@
      * listener you should use this method which 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>This API assumes the the {@link Binder#getCallingUid()} is the same as
+     * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
      * {@link Process#myUid()}.
      *
      * @param context Context for accessing resources.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d3ed006..a4ff18a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -752,6 +752,9 @@
 
     void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
 
+    IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+                String featureId, int userId);
+
     //------------------------------------------------------------------------
     //
     // The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2ed00b5..7101d1a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4470,12 +4470,17 @@
      * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
      * <code>null</code> if neither are found.
      *
+     * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+     * the caller is not allowed to query for the <code>packageName</code>.
+     *
      * @param packageName The name of the package to inspect.
      *
      * @return A fully-qualified {@link Intent} that can be used to launch the
      * main activity in the package. Returns <code>null</code> if the package
      * does not contain such an activity, or if <em>packageName</em> is not
      * recognized.
+     *
+     * @see #getLaunchIntentSenderForPackage(String)
      */
     public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
 
@@ -4510,6 +4515,28 @@
     public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
 
     /**
+     * Returns an {@link IntentSender} that can be used to launch a front-door activity in a
+     * package. This is used, for example, to implement an "open" button when browsing through
+     * packages. The current implementation is the same with
+     * {@link #getLaunchIntentForPackage(String)}. Instead of returning the {@link Intent}, it
+     * returns the {@link IntentSender} which is not restricted by the package visibility.
+     *
+     * <p>The caller can invoke
+     * {@link IntentSender#sendIntent(Context, int, Intent, IntentSender.OnFinished, Handler)}
+     * to launch the activity. An {@link IntentSender.SendIntentException} is thrown if the
+     * package does not contain such an activity, or if <em>packageName</em> is not recognized.
+     *
+     * @param packageName The name of the package to inspect.
+     * @return Returns a {@link IntentSender} to launch the activity.
+     *
+     * @see #getLaunchIntentForPackage(String)
+     */
+    public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+        throw new UnsupportedOperationException("getLaunchIntentSenderForPackage not implemented"
+                + "in subclass");
+    }
+
+    /**
      * Return an array of all of the POSIX secondary group IDs that have been
      * assigned to the given package.
      * <p>
@@ -6798,7 +6825,7 @@
     @NonNull
     public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
             Configuration configuration) throws NameNotFoundException {
-        throw new UnsupportedOperationException();
+        return getResourcesForApplication(app);
     }
 
     /**
@@ -9058,6 +9085,7 @@
     private static final class ApplicationInfoQuery {
         final String packageName;
         final int flags;
+        @UserIdInt
         final int userId;
 
         ApplicationInfoQuery(@Nullable String packageName, int flags, int userId) {
@@ -9119,6 +9147,10 @@
                             query.packageName, query.flags, query.userId);
                 }
                 @Override
+                protected boolean bypass(ApplicationInfoQuery query) {
+                    return query.userId < 0;
+                }
+                @Override
                 protected ApplicationInfo maybeCheckConsistency(
                         ApplicationInfoQuery query, ApplicationInfo proposedResult) {
                     // Implementing this debug check for ApplicationInfo would require a
@@ -9129,7 +9161,7 @@
 
     /** @hide */
     public static ApplicationInfo getApplicationInfoAsUserCached(
-            String packageName, int flags, int userId) {
+            String packageName, int flags, @UserIdInt int userId) {
         return sApplicationInfoCache.query(
                 new ApplicationInfoQuery(packageName, flags, userId));
     }
@@ -9161,6 +9193,7 @@
     private static final class PackageInfoQuery {
         final String packageName;
         final int flags;
+        @UserIdInt
         final int userId;
 
         PackageInfoQuery(@Nullable String packageName, int flags, int userId) {
@@ -9221,6 +9254,10 @@
                             query.packageName, query.flags, query.userId);
                 }
                 @Override
+                protected boolean bypass(PackageInfoQuery query) {
+                    return query.userId < 0;
+                }
+                @Override
                 protected PackageInfo maybeCheckConsistency(
                         PackageInfoQuery query, PackageInfo proposedResult) {
                     // Implementing this debug check for PackageInfo would require a
@@ -9231,7 +9268,7 @@
 
     /** @hide */
     public static PackageInfo getPackageInfoAsUserCached(
-            String packageName, int flags, int userId) {
+            String packageName, int flags, @UserIdInt int userId) {
         return sPackageInfoCache.query(new PackageInfoQuery(packageName, flags, userId));
     }
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ff2624..bed0383 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.split.SplitAssetLoader;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
@@ -884,7 +885,11 @@
         if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
             if (p.mSigningDetails != SigningDetails.UNKNOWN) {
                 // only return a valid SigningInfo if there is signing information to report
-                pi.signingInfo = new SigningInfo(p.mSigningDetails);
+                pi.signingInfo = new SigningInfo(
+                        new android.content.pm.SigningDetails(p.mSigningDetails.signatures,
+                                p.mSigningDetails.signatureSchemeVersion,
+                                p.mSigningDetails.publicKeys,
+                                p.mSigningDetails.pastSigningCertificates));
             } else {
                 pi.signingInfo = null;
             }
@@ -1399,24 +1404,34 @@
             // must use v2 signing scheme
             minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
         }
-        SigningDetails verified;
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<android.content.pm.SigningDetails> result;
         if (skipVerify) {
             // systemDir APKs are already trusted, save time by not verifying; since the signature
             // is not verified and some system apps can have their V2+ signatures stripped allow
             // pulling the certs from the jar signature.
-            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
-                        apkPath, SigningDetails.SignatureSchemeVersion.JAR);
+            result = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+                    input, apkPath, SigningDetails.SignatureSchemeVersion.JAR);
         } else {
-            verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
+            result = ApkSignatureVerifier.verify(input, apkPath, minSignatureScheme);
+        }
+        if (result.isError()) {
+            throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
+                    result.getException());
         }
 
         // Verify that entries are signed consistently with the first pkg
         // we encountered. Note that for splits, certificates may have
         // already been populated during an earlier parse of a base APK.
+        final android.content.pm.SigningDetails verified = result.getResult();
         if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
-            pkg.mSigningDetails = verified;
+            pkg.mSigningDetails = new SigningDetails(verified.getSignatures(),
+                    verified.getSignatureSchemeVersion(),
+                    verified.getPublicKeys(),
+                    verified.getPastSigningCertificates());
         } else {
-            if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+            if (!Signature.areExactMatch(pkg.mSigningDetails.signatures,
+                    verified.getSignatures())) {
                 throw new PackageParserException(
                         INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                         apkPath + " has mismatched certificates");
@@ -8686,6 +8701,22 @@
     // clean and move all the legacy code to one place.
 
     /**
+     * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+     * split APKs.
+     *
+     * @hide
+     * @deprecated Do not use. New changes should use
+     * {@link android.content.pm.split.SplitAssetLoader} instead.
+     */
+    @Deprecated
+    private interface SplitAssetLoader extends AutoCloseable {
+        AssetManager getBaseAssetManager() throws PackageParserException;
+        AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException;
+
+        ApkAssets getBaseApkAssets();
+    }
+
+    /**
      * A helper class that implements the dependency tree traversal for splits. Callbacks
      * are implemented by subclasses to notify whether a split has already been constructed
      * and is cached, and to actually create the split requested.
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 691c69c..a5d97f9 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -211,6 +211,8 @@
      * Additional flag for {@link #protectionLevel}, corresponding to the
      * {@code documenter} value of {@link android.R.attr#protectionLevel}.
      *
+     * @deprecated this protectionLevel is obsolete. Permissions previously granted
+     * through this protectionLevel have been migrated to use <code>role</code> instead
      * @hide
      */
     @SystemApi
@@ -309,7 +311,6 @@
             PROTECTION_FLAG_OEM,
             PROTECTION_FLAG_VENDOR_PRIVILEGED,
             PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
-            PROTECTION_FLAG_DOCUMENTER,
             PROTECTION_FLAG_CONFIGURATOR,
             PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
             PROTECTION_FLAG_APP_PREDICTOR,
@@ -561,9 +562,6 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
             protLevel.append("|textClassifier");
         }
-        if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
-            protLevel.append("|documenter");
-        }
         if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
             protLevel.append("|configurator");
         }
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index bce4b87..3f5c5d2 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,7 +256,7 @@
         try {
             if (obj != null) {
                 Signature other = (Signature)obj;
-                // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+                // Note, some classes, such as SigningDetails, rely on equals
                 // only comparing the mSignature arrays without the flags.
                 return this == other || Arrays.equals(mSignature, other.mSignature);
             }
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
new file mode 100644
index 0000000..584a058
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import libcore.util.HexEncoding;
+
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A container for signing-related data of an application package.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false, genParcelable = true, genAidl = false)
+public final class SigningDetails implements Parcelable {
+
+    private static final String TAG = "SigningDetails";
+
+    @IntDef({SignatureSchemeVersion.UNKNOWN,
+            SignatureSchemeVersion.JAR,
+            SignatureSchemeVersion.SIGNING_BLOCK_V2,
+            SignatureSchemeVersion.SIGNING_BLOCK_V3,
+            SignatureSchemeVersion.SIGNING_BLOCK_V4})
+    public @interface SignatureSchemeVersion {
+        int UNKNOWN = 0;
+        int JAR = 1;
+        int SIGNING_BLOCK_V2 = 2;
+        int SIGNING_BLOCK_V3 = 3;
+        int SIGNING_BLOCK_V4 = 4;
+    }
+
+    /** The signing certificates associated with this application package. */
+    private final @Nullable Signature[] mSignatures;
+
+    /** The signature scheme version for this application package. */
+    private final @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+    /** The public keys set for the certificates. */
+    private final @Nullable ArraySet<PublicKey> mPublicKeys;
+
+    /**
+     * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+     * contains two pieces of information:
+     *   1) the past signing certificates
+     *   2) the flags that APK wants to assign to each of the past signing certificates.
+     *
+     * This collection of {@code Signature} objects, each of which is formed from a former
+     * signing certificate of this APK before it was changed by signing certificate rotation,
+     * represents the first piece of information.  It is the APK saying to the rest of the
+     * world: "hey if you trust the old cert, you can trust me!"  This is useful, if for
+     * instance, the platform would like to determine whether or not to allow this APK to do
+     * something it would've allowed it to do under the old cert (like upgrade).
+     */
+    private final @Nullable Signature[] mPastSigningCertificates;
+
+    /** special value used to see if cert is in package - not exposed to callers */
+    private static final int PAST_CERT_EXISTS = 0;
+
+    @IntDef(flag = true,
+            value = {CertCapabilities.INSTALLED_DATA,
+                    CertCapabilities.SHARED_USER_ID,
+                    CertCapabilities.PERMISSION,
+                    CertCapabilities.ROLLBACK})
+    public @interface CertCapabilities {
+
+        /** accept data from already installed pkg with this cert */
+        int INSTALLED_DATA = 1;
+
+        /** accept sharedUserId with pkg with this cert */
+        int SHARED_USER_ID = 2;
+
+        /** grant SIGNATURE permissions to pkgs with this cert */
+        int PERMISSION = 4;
+
+        /** allow pkg to update to one signed by this certificate */
+        int ROLLBACK = 8;
+
+        /** allow pkg to continue to have auth access gated by this cert */
+        int AUTH = 16;
+    }
+
+    /** A representation of unknown signing details. Use instead of null. */
+    public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
+            SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
+
+    @VisibleForTesting
+    public SigningDetails(@Nullable Signature[] signatures,
+            @SignatureSchemeVersion int signatureSchemeVersion,
+            @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates) {
+        mSignatures = signatures;
+        mSignatureSchemeVersion = signatureSchemeVersion;
+        mPublicKeys = keys;
+        mPastSigningCertificates = pastSigningCertificates;
+    }
+
+    public SigningDetails(@Nullable Signature[] signatures,
+            @SignatureSchemeVersion int signatureSchemeVersion,
+            @Nullable Signature[] pastSigningCertificates)
+            throws CertificateException {
+        this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+                pastSigningCertificates);
+    }
+
+    public SigningDetails(@Nullable Signature[] signatures,
+            @SignatureSchemeVersion int signatureSchemeVersion)
+            throws CertificateException {
+        this(signatures, signatureSchemeVersion, /* pastSigningCertificates */ null);
+    }
+
+    public SigningDetails(@Nullable SigningDetails orig) {
+        if (orig != null) {
+            if (orig.mSignatures != null) {
+                mSignatures = orig.mSignatures.clone();
+            } else {
+                mSignatures = null;
+            }
+            mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+            mPublicKeys = new ArraySet<>(orig.mPublicKeys);
+            if (orig.mPastSigningCertificates != null) {
+                mPastSigningCertificates = orig.mPastSigningCertificates.clone();
+            } else {
+                mPastSigningCertificates = null;
+            }
+        } else {
+            mSignatures = null;
+            mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+            mPublicKeys = null;
+            mPastSigningCertificates = null;
+        }
+    }
+
+    /**
+     * Merges the signing lineage of this instance with the lineage in the provided {@code
+     * otherSigningDetails} when one has the same or an ancestor signer of the other.
+     *
+     * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
+     * containing the longest common lineage with the most restrictive capabilities. If the two
+     * lineages contain the same signers with the same capabilities then the instance on which
+     * this was invoked is returned without any changes. Similarly if neither instance has a
+     * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+     *
+     * Following are some example results of this method for lineages with signers A, B, C, D:
+     * - lineage B merged with lineage A -> B returns lineage A -> B.
+     * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+     * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+     *  lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
+     *  lineage A -> B with both capabilities revoked for A.
+     * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+     *  A -> B -> C since the current signer of both instances is not the same or in the
+     *   lineage of the other.
+     *
+     * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+     * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
+     *         of the other. If neither instance has a lineage, or if neither has the same or an
+     *         ancestor signer then this instance is returned.
+     */
+    public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+        if (!hasPastSigningCertificates()) {
+            return otherSigningDetails.hasPastSigningCertificates()
+                    && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
+        }
+        if (!otherSigningDetails.hasPastSigningCertificates()) {
+            return this;
+        }
+        // Use the utility method to determine which SigningDetails instance is the descendant
+        // and to confirm that the signing lineage does not diverge.
+        SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
+        if (descendantSigningDetails == null) {
+            return this;
+        }
+        return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
+                otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+    }
+
+    /**
+     * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
+     * signer in the provided {@code otherSigningDetails}.
+     *
+     * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+     * @return Merged {@code SigningDetails} instance.
+     */
+    private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
+            @NonNull SigningDetails otherSigningDetails) {
+        // This method should only be called with instances that contain lineages.
+        int index = mPastSigningCertificates.length - 1;
+        int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
+        if (index < 0 || otherIndex < 0) {
+            return this;
+        }
+
+        List<Signature> mergedSignatures = new ArrayList<>();
+        boolean capabilitiesModified = false;
+        // If this is a descendant lineage then add all of the descendant signer(s) to the
+        // merged lineage until the ancestor signer is reached.
+        while (index >= 0 && !mPastSigningCertificates[index].equals(
+                otherSigningDetails.mPastSigningCertificates[otherIndex])) {
+            mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+        }
+        // If the signing lineage was exhausted then the provided ancestor is not actually an
+        // ancestor of this lineage.
+        if (index < 0) {
+            return this;
+        }
+
+        do {
+            // Add the common signer to the merged lineage with the most restrictive
+            // capabilities of the two lineages.
+            Signature signature = mPastSigningCertificates[index--];
+            Signature ancestorSignature =
+                    otherSigningDetails.mPastSigningCertificates[otherIndex--];
+            Signature mergedSignature = new Signature(signature);
+            int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
+            if (signature.getFlags() != mergedCapabilities) {
+                capabilitiesModified = true;
+                mergedSignature.setFlags(mergedCapabilities);
+            }
+            mergedSignatures.add(mergedSignature);
+        } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
+                otherSigningDetails.mPastSigningCertificates[otherIndex]));
+
+        // If both lineages still have elements then their lineages have diverged; since this is
+        // not supported return the invoking instance.
+        if (index >= 0 && otherIndex >= 0) {
+            return this;
+        }
+
+        // Add any remaining elements from either lineage that is not yet exhausted to the
+        // the merged lineage.
+        while (otherIndex >= 0) {
+            mergedSignatures.add(new Signature(
+                    otherSigningDetails.mPastSigningCertificates[otherIndex--]));
+        }
+        while (index >= 0) {
+            mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+        }
+
+        // if this lineage already contains all the elements in the ancestor and none of the
+        // capabilities were changed then just return this instance.
+        if (mergedSignatures.size() == mPastSigningCertificates.length
+                && !capabilitiesModified) {
+            return this;
+        }
+        // Since the signatures were added to the merged lineage from newest to oldest reverse
+        // the list to ensure the oldest signer is at index 0.
+        Collections.reverse(mergedSignatures);
+        try {
+            return new SigningDetails(new Signature[]{new Signature(mSignatures[0])},
+                    mSignatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
+        } catch (CertificateException e) {
+            Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
+            return this;
+        }
+    }
+
+    /**
+     * Returns whether this and the provided {@code otherSigningDetails} share a common
+     * ancestor.
+     *
+     * <p>The two SigningDetails have a common ancestor if any of the following conditions are
+     * met:
+     * - If neither has a lineage and their current signer(s) are equal.
+     * - If only one has a lineage and the signer of the other is the same or in the lineage.
+     * - If both have a lineage and their current signers are the same or one is in the lineage
+     * of the other, and their lineages do not diverge to different signers.
+     */
+    public boolean hasCommonAncestor(@NonNull SigningDetails otherSigningDetails) {
+        if (!hasPastSigningCertificates()) {
+            // If this instance does not have a lineage then it must either be in the ancestry
+            // of or the same signer of the otherSigningDetails.
+            return otherSigningDetails.hasAncestorOrSelf(this);
+        }
+        if (!otherSigningDetails.hasPastSigningCertificates()) {
+            return hasAncestorOrSelf(otherSigningDetails);
+        }
+        // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
+        // details; a null return from that method indicates there is no common lineage between
+        // the two or that they diverge at a point in the lineage.
+        return getDescendantOrSelf(otherSigningDetails) != null;
+    }
+
+    /**
+     * Returns whether this instance is currently signed, or has ever been signed, with a
+     * signing certificate from the provided {@link Set} of {@code certDigests}.
+     *
+     * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding
+     * of each trusted certificate with the digest characters in upper case. If this instance
+     * has multiple signers then all signers must be in the provided {@code Set}. If this
+     * instance has a signing lineage then this method will return true if any of the previous
+     * signers in the lineage match one of the entries in the {@code Set}.
+     */
+    public boolean hasAncestorOrSelfWithDigest(@Nullable Set<String> certDigests) {
+        if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) {
+            return false;
+        }
+        // If an app is signed by multiple signers then all of the signers must be in the Set.
+        if (mSignatures.length > 1) {
+            // If the Set has less elements than the number of signatures then immediately
+            // return false as there's no way to satisfy the requirement of all signatures being
+            // in the Set.
+            if (certDigests.size() < mSignatures.length) {
+                return false;
+            }
+            for (Signature signature : mSignatures) {
+                String signatureDigest = PackageUtils.computeSha256Digest(
+                        signature.toByteArray());
+                if (!certDigests.contains(signatureDigest)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        String signatureDigest = PackageUtils.computeSha256Digest(mSignatures[0].toByteArray());
+        if (certDigests.contains(signatureDigest)) {
+            return true;
+        }
+        if (hasPastSigningCertificates()) {
+            // The last element in the pastSigningCertificates array is the current signer;
+            // since that was verified above just check all the signers in the lineage.
+            for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+                signatureDigest = PackageUtils.computeSha256Digest(
+                        mPastSigningCertificates[i].toByteArray());
+                if (certDigests.contains(signatureDigest)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the SigningDetails with a descendant (or same) signer after verifying the
+     * descendant has the same, a superset, or a subset of the lineage of the ancestor.
+     *
+     * <p>If this instance and the provided {@code otherSigningDetails} do not share an
+     * ancestry, or if their lineages diverge then null is returned to indicate there is no
+     * valid descendant SigningDetails.
+     */
+    private @Nullable SigningDetails getDescendantOrSelf(
+            @NonNull SigningDetails otherSigningDetails) {
+        final SigningDetails descendantSigningDetails;
+        final SigningDetails ancestorSigningDetails;
+        if (hasAncestorOrSelf(otherSigningDetails)) {
+            // If the otherSigningDetails has the same signer or a signer in the lineage of this
+            // instance then treat this instance as the descendant.
+            descendantSigningDetails = this;
+            ancestorSigningDetails = otherSigningDetails;
+        } else if (otherSigningDetails.hasAncestor(this)) {
+            // The above check confirmed that the two instances do not have the same signer and
+            // the signer of otherSigningDetails is not in this instance's lineage; if this
+            // signer is in the otherSigningDetails lineage then treat this as the ancestor.
+            descendantSigningDetails = otherSigningDetails;
+            ancestorSigningDetails = this;
+        } else {
+            // The signers are not the same and neither has the current signer of the other in
+            // its lineage; return null to indicate there is no descendant signer.
+            return null;
+        }
+        // Once the descent (or same) signer is identified iterate through the ancestry until
+        // the current signer of the ancestor is found.
+        int descendantIndex = descendantSigningDetails.mPastSigningCertificates.length - 1;
+        int ancestorIndex = ancestorSigningDetails.mPastSigningCertificates.length - 1;
+        while (descendantIndex >= 0
+                && !descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+                ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])) {
+            descendantIndex--;
+        }
+        // Since the ancestry was verified above the descendant lineage should never be
+        // exhausted, but if for some reason the ancestor signer is not found then return null.
+        if (descendantIndex < 0) {
+            return null;
+        }
+        // Once the common ancestor (or same) signer is found iterate over the lineage of both
+        // to ensure that they are either the same or one is a subset of the other.
+        do {
+            descendantIndex--;
+            ancestorIndex--;
+        } while (descendantIndex >= 0 && ancestorIndex >= 0
+                && descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+                ancestorSigningDetails.mPastSigningCertificates[ancestorIndex]));
+
+        // If both lineages still have elements then they diverge and cannot be considered a
+        // valid common lineage.
+        if (descendantIndex >= 0 && ancestorIndex >= 0) {
+            return null;
+        }
+        // Since one or both of the lineages was exhausted they are either the same or one is a
+        // subset of the other; return the valid descendant.
+        return descendantSigningDetails;
+    }
+
+    /** Returns true if the signing details have one or more signatures. */
+    public boolean hasSignatures() {
+        return mSignatures != null && mSignatures.length > 0;
+    }
+
+    /** Returns true if the signing details have past signing certificates. */
+    public boolean hasPastSigningCertificates() {
+        return mPastSigningCertificates != null && mPastSigningCertificates.length > 0;
+    }
+
+    /**
+     * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+     * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+     * then that means it has authorized a signing certificate rotation, which eventually leads
+     * to our certificate, and thus can be trusted. If this method evaluates to true, this
+     * SigningDetails object should be trusted if the previous one is.
+     */
+    public boolean hasAncestorOrSelf(@NonNull SigningDetails oldDetails) {
+        if (this == UNKNOWN || oldDetails == UNKNOWN) {
+            return false;
+        }
+        if (oldDetails.mSignatures.length > 1) {
+            // multiple-signer packages cannot rotate signing certs, so we just compare current
+            // signers for an exact match
+            return signaturesMatchExactly(oldDetails);
+        } else {
+            // we may have signing certificate rotation history, check to see if the oldDetails
+            // was one of our old signing certificates
+            return hasCertificate(oldDetails.mSignatures[0]);
+        }
+    }
+
+    /**
+     * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+     * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+     * determine if this object is newer than the provided one.
+     */
+    public boolean hasAncestor(@NonNull SigningDetails oldDetails) {
+        if (this == UNKNOWN || oldDetails == UNKNOWN) {
+            return false;
+        }
+        if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+            // the last entry in pastSigningCertificates is the current signer, ignore it
+            for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+                if (mPastSigningCertificates[i].equals(oldDetails.mSignatures[0])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether this {@code SigningDetails} has a signer in common with the provided
+     * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+     * signer.
+     *
+     * <p>Note this method allows for the signing lineage to diverge, so this should only be
+     * used for instances where the only requirement is a common signer in the lineage with
+     * the specified capabilities. If the current signer of this instance is an ancestor of
+     * {@code otherDetails} then {@code true} is immediately returned since the current signer
+     * has all capabilities granted.
+     */
+    public boolean hasCommonSignerWithCapability(@NonNull SigningDetails otherDetails,
+            @CertCapabilities int flags) {
+        if (this == UNKNOWN || otherDetails == UNKNOWN) {
+            return false;
+        }
+        // If either is signed with more than one signer then both must be signed by the same
+        // signers to consider the capabilities granted.
+        if (mSignatures.length > 1 || otherDetails.mSignatures.length > 1) {
+            return signaturesMatchExactly(otherDetails);
+        }
+        // The Signature class does not use the granted capabilities in the hashCode
+        // computation, so a Set can be used to check for a common signer.
+        Set<Signature> otherSignatures = new ArraySet<>();
+        if (otherDetails.hasPastSigningCertificates()) {
+            otherSignatures.addAll(Arrays.asList(otherDetails.mPastSigningCertificates));
+        } else {
+            otherSignatures.addAll(Arrays.asList(otherDetails.mSignatures));
+        }
+        // If the current signer of this instance is an ancestor of the other than return true
+        // since all capabilities are granted to the current signer.
+        if (otherSignatures.contains(mSignatures[0])) {
+            return true;
+        }
+        if (hasPastSigningCertificates()) {
+            // Since the current signer was checked above and the last signature in the
+            // pastSigningCertificates is the current signer skip checking the last element.
+            for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+                if (otherSignatures.contains(mPastSigningCertificates[i])) {
+                    // If the caller specified multiple capabilities ensure all are set.
+                    if ((mPastSigningCertificates[i].getFlags() & flags) == flags) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+     * not this one grants it the provided capability, represented by the {@code flags}
+     * parameter.  In the event of signing certificate rotation, a package may still interact
+     * with entities signed by its old signing certificate and not want to break previously
+     * functioning behavior.  The {@code flags} value determines which capabilities the app
+     * signed by the newer signing certificate would like to continue to give to its previous
+     * signing certificate(s).
+     */
+    public boolean checkCapability(@NonNull SigningDetails oldDetails,
+            @CertCapabilities int flags) {
+        if (this == UNKNOWN || oldDetails == UNKNOWN) {
+            return false;
+        }
+        if (oldDetails.mSignatures.length > 1) {
+            // multiple-signer packages cannot rotate signing certs, so we must have an exact
+            // match, which also means all capabilities are granted
+            return signaturesMatchExactly(oldDetails);
+        } else {
+            // we may have signing certificate rotation history, check to see if the oldDetails
+            // was one of our old signing certificates, and if we grant it the capability it's
+            // requesting
+            return hasCertificate(oldDetails.mSignatures[0], flags);
+        }
+    }
+
+    /**
+     * A special case of {@code checkCapability} which re-encodes both sets of signing
+     * certificates to counteract a previous re-encoding.
+     */
+    public boolean checkCapabilityRecover(@NonNull SigningDetails oldDetails,
+            @CertCapabilities int flags) throws CertificateException {
+        if (oldDetails == UNKNOWN || this == UNKNOWN) {
+            return false;
+        }
+        if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+            // signing certificates may have rotated, check entire history for effective match
+            for (int i = 0; i < mPastSigningCertificates.length; i++) {
+                if (Signature.areEffectiveMatch(
+                        oldDetails.mSignatures[0],
+                        mPastSigningCertificates[i])
+                        && mPastSigningCertificates[i].getFlags() == flags) {
+                    return true;
+                }
+            }
+        } else {
+            return Signature.areEffectiveMatch(oldDetails.mSignatures, mSignatures);
+        }
+        return false;
+    }
+
+    /**
+     * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+     * including the current signer.  Automatically returns false if this object has multiple
+     * signing certificates, since rotation is only supported for single-signers; this is
+     * enforced by {@code hasCertificateInternal}.
+     */
+    public boolean hasCertificate(@NonNull Signature signature) {
+        return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+    }
+
+    /**
+     * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+     * including the current signer, and whether or not it has the given permission.
+     * Certificates which match our current signer automatically get all capabilities.
+     * Automatically returns false if this object has multiple signing certificates, since
+     * rotation is only supported for single-signers.
+     */
+    public boolean hasCertificate(@NonNull Signature signature, @CertCapabilities int flags) {
+        return hasCertificateInternal(signature, flags);
+    }
+
+    /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+    public boolean hasCertificate(byte[] certificate) {
+        Signature signature = new Signature(certificate);
+        return hasCertificate(signature);
+    }
+
+    private boolean hasCertificateInternal(@NonNull Signature signature, int flags) {
+        if (this == UNKNOWN) {
+            return false;
+        }
+
+        // only single-signed apps can have pastSigningCertificates
+        if (hasPastSigningCertificates()) {
+            // check all past certs, except for the current one, which automatically gets all
+            // capabilities, since it is the same as the current signature
+            for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+                if (mPastSigningCertificates[i].equals(signature)) {
+                    if (flags == PAST_CERT_EXISTS
+                            || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        // not in previous certs signing history, just check the current signer and make sure
+        // we are singly-signed
+        return mSignatures.length == 1 && mSignatures[0].equals(signature);
+    }
+
+    /**
+     * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+     * or not this one grants it the provided capability, represented by the {@code flags}
+     * parameter.  In the event of signing certificate rotation, a package may still interact
+     * with entities signed by its old signing certificate and not want to break previously
+     * functioning behavior.  The {@code flags} value determines which capabilities the app
+     * signed by the newer signing certificate would like to continue to give to its previous
+     * signing certificate(s).
+     *
+     * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+     *                     app with multiple signers, this represents the hex-encoded sha256
+     *                     digest of the combined hex-encoded sha256 digests of each individual
+     *                     signing certificate according to {@link
+     *                     PackageUtils#computeSignaturesSha256Digest(Signature[])}
+     */
+    public boolean checkCapability(@Nullable String sha256String, @CertCapabilities int flags) {
+        if (this == UNKNOWN || TextUtils.isEmpty(sha256String)) {
+            return false;
+        }
+
+        // first see if the hash represents a single-signer in our signing history
+        final byte[] sha256Bytes = HexEncoding.decode(sha256String, false /* allowSingleChar */);
+        if (hasSha256Certificate(sha256Bytes, flags)) {
+            return true;
+        }
+
+        // Not in signing history, either represents multiple signatures or not a match.
+        // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+        // We already check the single-signer case above as part of hasSha256Certificate, so no
+        // need to verify we have multiple signers, just run the old check
+        // just consider current signing certs
+        final String[] mSignaturesSha256Digests =
+                PackageUtils.computeSignaturesSha256Digests(mSignatures);
+        final String mSignaturesSha256Digest =
+                PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+        return mSignaturesSha256Digest.equals(sha256String);
+    }
+
+    /**
+     * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+     * history, including the current signer.  Automatically returns false if this object has
+     * multiple signing certificates, since rotation is only supported for single-signers.
+     */
+    public boolean hasSha256Certificate(byte[] sha256Certificate) {
+        return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+    }
+
+    /**
+     * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+     * certificate in this SigningDetails' signing certificate history, including the current
+     * signer, and whether or not it has the given permission.  Certificates which match our
+     * current signer automatically get all capabilities. Automatically returns false if this
+     * object has multiple signing certificates, since rotation is only supported for
+     * single-signers.
+     */
+    public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+        return hasSha256CertificateInternal(sha256Certificate, flags);
+    }
+
+    private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+        if (this == UNKNOWN) {
+            return false;
+        }
+        if (hasPastSigningCertificates()) {
+            // check all past certs, except for the last one, which automatically gets all
+            // capabilities, since it is the same as the current signature, and is checked below
+            for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+                byte[] digest = PackageUtils.computeSha256DigestBytes(
+                        mPastSigningCertificates[i].toByteArray());
+                if (Arrays.equals(sha256Certificate, digest)) {
+                    if (flags == PAST_CERT_EXISTS
+                            || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        // not in previous certs signing history, just check the current signer
+        if (mSignatures.length == 1) {
+            byte[] digest = PackageUtils.computeSha256DigestBytes(mSignatures[0].toByteArray());
+            return Arrays.equals(sha256Certificate, digest);
+        }
+        return false;
+    }
+
+    /** Returns true if the signatures in this and other match exactly. */
+    public boolean signaturesMatchExactly(@NonNull SigningDetails other) {
+        return Signature.areExactMatch(mSignatures, other.mSignatures);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        boolean isUnknown = UNKNOWN == this;
+        dest.writeBoolean(isUnknown);
+        if (isUnknown) {
+            return;
+        }
+        dest.writeTypedArray(mSignatures, flags);
+        dest.writeInt(mSignatureSchemeVersion);
+        dest.writeArraySet(mPublicKeys);
+        dest.writeTypedArray(mPastSigningCertificates, flags);
+    }
+
+    protected SigningDetails(@NonNull Parcel in) {
+        final ClassLoader boot = Object.class.getClassLoader();
+        mSignatures = in.createTypedArray(Signature.CREATOR);
+        mSignatureSchemeVersion = in.readInt();
+        mPublicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+        mPastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+    }
+
+    public static final @NonNull Parcelable.Creator<SigningDetails> CREATOR =
+            new Creator<SigningDetails>() {
+                @Override
+                public SigningDetails createFromParcel(@NonNull Parcel source) {
+                    if (source.readBoolean()) {
+                        return UNKNOWN;
+                    }
+                    return new SigningDetails(source);
+                }
+
+                @Override
+                public SigningDetails[] newArray(int size) {
+                    return new SigningDetails[size];
+                }
+            };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SigningDetails)) return false;
+
+        final SigningDetails that = (SigningDetails) o;
+
+        if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false;
+        if (!Signature.areExactMatch(mSignatures, that.mSignatures)) return false;
+        if (mPublicKeys != null) {
+            if (!mPublicKeys.equals((that.mPublicKeys))) {
+                return false;
+            }
+        } else if (that.mPublicKeys != null) {
+            return false;
+        }
+
+        // can't use Signature.areExactMatch() because order matters with the past signing certs
+        if (!Arrays.equals(mPastSigningCertificates, that.mPastSigningCertificates)) {
+            return false;
+        }
+        // The capabilities for the past signing certs must match as well.
+        for (int i = 0; i < mPastSigningCertificates.length; i++) {
+            if (mPastSigningCertificates[i].getFlags()
+                    != that.mPastSigningCertificates[i].getFlags()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = +Arrays.hashCode(mSignatures);
+        result = 31 * result + mSignatureSchemeVersion;
+        result = 31 * result + (mPublicKeys != null ? mPublicKeys.hashCode() : 0);
+        result = 31 * result + Arrays.hashCode(mPastSigningCertificates);
+        return result;
+    }
+
+    /**
+     * Builder of {@code SigningDetails} instances.
+     */
+    public static class Builder {
+        private @NonNull Signature[] mSignatures;
+        private @SignatureSchemeVersion int mSignatureSchemeVersion =
+                SignatureSchemeVersion.UNKNOWN;
+        private @Nullable Signature[] mPastSigningCertificates;
+
+        public Builder() {
+        }
+
+        /** get signing certificates used to sign the current APK */
+        public SigningDetails.Builder setSignatures(@NonNull Signature[] signatures) {
+            mSignatures = signatures;
+            return this;
+        }
+
+        /** set the signature scheme version used to sign the APK */
+        public SigningDetails.Builder setSignatureSchemeVersion(
+                @SignatureSchemeVersion int signatureSchemeVersion) {
+            mSignatureSchemeVersion = signatureSchemeVersion;
+            return this;
+        }
+
+        /** set the signing certificates by which the APK proved it can be authenticated */
+        public SigningDetails.Builder setPastSigningCertificates(
+                @Nullable Signature[] pastSigningCertificates) {
+            mPastSigningCertificates = pastSigningCertificates;
+            return this;
+        }
+
+        private void checkInvariants() {
+            // must have signatures and scheme version set
+            if (mSignatures == null) {
+                throw new IllegalStateException("SigningDetails requires the current signing"
+                        + " certificates.");
+            }
+        }
+        /** build a {@code SigningDetails} object */
+        public SigningDetails build()
+                throws CertificateException {
+            checkInvariants();
+            return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+                    mPastSigningCertificates);
+        }
+    }
+
+    /** Parses the public keys from the set of signatures. */
+    public static ArraySet<PublicKey> toSigningKeys(@NonNull Signature[] signatures)
+            throws CertificateException {
+        final ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+        for (int i = 0; i < signatures.length; i++) {
+            keys.add(signatures[i].getPublicKey());
+        }
+        return keys;
+    }
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/SigningDetails.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * The signing certificates associated with this application package.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Signature[] getSignatures() {
+        return mSignatures;
+    }
+
+    /**
+     * The signature scheme version for this application package.
+     */
+    @DataClass.Generated.Member
+    public @SignatureSchemeVersion int getSignatureSchemeVersion() {
+        return mSignatureSchemeVersion;
+    }
+
+    /**
+     * The public keys set for the certificates.
+     */
+    @DataClass.Generated.Member
+    public @Nullable ArraySet<PublicKey> getPublicKeys() {
+        return mPublicKeys;
+    }
+
+    /**
+     * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+     * contains two pieces of information:
+     *   1) the past signing certificates
+     *   2) the flags that APK wants to assign to each of the past signing certificates.
+     *
+     * This collection of {@code Signature} objects, each of which is formed from a former
+     * signing certificate of this APK before it was changed by signing certificate rotation,
+     * represents the first piece of information.  It is the APK saying to the rest of the
+     * world: "hey if you trust the old cert, you can trust me!"  This is useful, if for
+     * instance, the platform would like to determine whether or not to allow this APK to do
+     * something it would've allowed it to do under the old cert (like upgrade).
+     */
+    @DataClass.Generated.Member
+    public @Nullable Signature[] getPastSigningCertificates() {
+        return mPastSigningCertificates;
+    }
+
+    @DataClass.Generated(
+            time = 1616984092921L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
+            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final  int PAST_CERT_EXISTS\npublic static final  android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic  boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic  boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic  boolean hasSignatures()\npublic  boolean hasPastSigningCertificates()\npublic  boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic  boolean hasAncestor(android.content.pm.SigningDetails)\npublic  boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic  boolean checkCapability(android.content.pm.SigningDetails,int)\npublic  boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic  boolean hasCertificate(android.content.pm.Signature)\npublic  boolean hasCertificate(android.content.pm.Signature,int)\npublic  boolean hasCertificate(byte[])\nprivate  boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic  boolean checkCapability(java.lang.String,int)\npublic  boolean hasSha256Certificate(byte[])\npublic  boolean hasSha256Certificate(byte[],int)\nprivate  boolean hasSha256CertificateInternal(byte[],int)\npublic  boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static  android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic  android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic  android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic  android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate  void checkInvariants()\npublic  android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index d14be9c..7459a90 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,7 +16,6 @@
 
 package android.content.pm;
 
-
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,25 +26,25 @@
 public final class SigningInfo implements Parcelable {
 
     @NonNull
-    private final PackageParser.SigningDetails mSigningDetails;
+    private final SigningDetails mSigningDetails;
 
     public SigningInfo() {
-        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+        mSigningDetails = SigningDetails.UNKNOWN;
     }
 
     /**
      * @hide only packagemanager should be populating this
      */
-    public SigningInfo(PackageParser.SigningDetails signingDetails) {
-        mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+    public SigningInfo(SigningDetails signingDetails) {
+        mSigningDetails = new SigningDetails(signingDetails);
     }
 
     public SigningInfo(SigningInfo orig) {
-        mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+        mSigningDetails = new SigningDetails(orig.mSigningDetails);
     }
 
     private SigningInfo(Parcel source) {
-        mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+        mSigningDetails = SigningDetails.CREATOR.createFromParcel(source);
     }
 
     /**
@@ -53,7 +52,8 @@
      * their identity is viewed as being the set of all signers, not just any one.
      */
     public boolean hasMultipleSigners() {
-        return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+        return mSigningDetails.getSignatures() != null
+                && mSigningDetails.getSignatures().length > 1;
     }
 
     /**
@@ -65,8 +65,8 @@
      * signing history, since it could change to a new signing certificate at any time.
      */
     public boolean hasPastSigningCertificates() {
-        return mSigningDetails.signatures != null
-                && mSigningDetails.pastSigningCertificates != null;
+        return mSigningDetails.getPastSigningCertificates() != null
+                && mSigningDetails.getPastSigningCertificates().length > 0;
     }
 
     /**
@@ -93,11 +93,11 @@
         } else if (!hasPastSigningCertificates()) {
 
             // this package is only signed by one signer with no history, return it
-            return mSigningDetails.signatures;
+            return mSigningDetails.getSignatures();
         } else {
 
             // this package has provided proof of past signing certificates, include them
-            return mSigningDetails.pastSigningCertificates;
+            return mSigningDetails.getPastSigningCertificates();
         }
     }
 
@@ -111,7 +111,7 @@
      * </note>
      */
     public Signature[] getApkContentsSigners() {
-        return mSigningDetails.signatures;
+        return mSigningDetails.getSignatures();
     }
 
     @Override
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 8f9a0d7..e75aa06 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -19,9 +19,10 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
 import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.JsonReader;
@@ -176,18 +177,16 @@
      * Validate that the given file is a dex metadata archive.
      * This is just a validation that the file is a zip archive that contains a manifest.json
      * with the package name and version code.
-     *
-     * @throws PackageParserException if the file is not a .dm file.
      */
-    public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode)
-            throws PackageParserException {
-        validateDexMetadataFile(dmaPath, packageName, versionCode,
+    public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
+            String packageName, long versionCode) {
+        return validateDexMetadataFile(input, dmaPath, packageName, versionCode,
                SystemProperties.getBoolean(PROPERTY_DM_JSON_MANIFEST_REQUIRED, false));
     }
 
     @VisibleForTesting
-    public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode,
-            boolean requireManifest) throws PackageParserException {
+    public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
+            String packageName, long versionCode, boolean requireManifest) {
         StrictJarFile jarFile = null;
 
         if (DEBUG) {
@@ -197,11 +196,10 @@
 
         try {
             jarFile = new StrictJarFile(dmaPath, false, false);
-            validateDexMetadataManifest(dmaPath, jarFile, packageName, versionCode,
+            return validateDexMetadataManifest(input, dmaPath, jarFile, packageName, versionCode,
                     requireManifest);
         } catch (IOException e) {
-            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
-                    "Error opening " + dmaPath, e);
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA, "Error opening " + dmaPath, e);
         } finally {
             if (jarFile != null) {
                 try {
@@ -213,21 +211,21 @@
     }
 
     /** Ensure that packageName and versionCode match the manifest.json in the .dm file */
-    private static void validateDexMetadataManifest(String dmaPath, StrictJarFile jarFile,
-            String packageName, long versionCode, boolean requireManifest)
-            throws IOException, PackageParserException {
+    private static ParseResult validateDexMetadataManifest(ParseInput input, String dmaPath,
+            StrictJarFile jarFile, String packageName, long versionCode, boolean requireManifest)
+            throws IOException {
         if (!requireManifest) {
             if (DEBUG) {
                 Log.v(TAG, "validateDexMetadataManifest: " + dmaPath
                         + " manifest.json check skipped");
             }
-            return;
+            return input.success(null);
         }
 
         ZipEntry zipEntry = jarFile.findEntry("manifest.json");
         if (zipEntry == null) {
-              throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
-                      "Missing manifest.json in " + dmaPath);
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
+                    "Missing manifest.json in " + dmaPath);
         }
         InputStream inputStream = jarFile.getInputStream(zipEntry);
 
@@ -235,7 +233,7 @@
         try {
           reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
         } catch (UnsupportedEncodingException e) {
-            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                     "Error opening manifest.json in " + dmaPath, e);
         }
         String jsonPackageName = null;
@@ -255,19 +253,19 @@
         reader.endObject();
 
         if (jsonPackageName == null || jsonVersionCode == -1) {
-            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                     "manifest.json in " + dmaPath
                     + " is missing 'packageName' and/or 'versionCode'");
         }
 
         if (!jsonPackageName.equals(packageName)) {
-            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                     "manifest.json in " + dmaPath + " has invalid packageName: " + jsonPackageName
                     + ", expected: " + packageName);
         }
 
         if (versionCode != jsonVersionCode) {
-            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                     "manifest.json in " + dmaPath + " has invalid versionCode: " + jsonVersionCode
                     + ", expected: " + versionCode);
         }
@@ -276,6 +274,7 @@
             Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName +
                     ", " + versionCode + ": successful");
         }
+        return input.success(null);
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index d8ec512..024c18c 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 
 import com.android.internal.util.DataClass;
@@ -398,10 +398,10 @@
     }
 
     @DataClass.Generated(
-            time = 1610596637723L,
+            time = 1616985847981L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.PackageParser.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final  int mRollbackDataPolicy\npublic  long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final  int mRollbackDataPolicy\npublic  long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 154d923..01c0a88 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
@@ -301,16 +302,15 @@
 
             parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
 
-            final PackageParser.SigningDetails signingDetails;
+            final SigningDetails signingDetails;
             if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
                 final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                 try {
-                    ParseResult<PackageParser.SigningDetails> result =
-                            ParsingPackageUtils.getSigningDetails(input,
-                                    apkFile.getAbsolutePath(), skipVerify, false,
-                                    PackageParser.SigningDetails.UNKNOWN,
-                                    DEFAULT_TARGET_SDK_VERSION);
+                    final ParseResult<SigningDetails> result =
+                            ParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),
+                                    skipVerify, /* isStaticSharedLibrary */ false,
+                                    SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
                     if (result.isError()) {
                         return input.error(result);
                     }
@@ -319,7 +319,7 @@
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             } else {
-                signingDetails = PackageParser.SigningDetails.UNKNOWN;
+                signingDetails = SigningDetails.UNKNOWN;
             }
 
             return parseApkLite(input, apkPath, parser, signingDetails);
@@ -340,7 +340,7 @@
     }
 
     private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
-            XmlResourceParser parser, PackageParser.SigningDetails signingDetails)
+            XmlResourceParser parser, SigningDetails signingDetails)
             throws IOException, XmlPullParserException {
         ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
         if (result.isError()) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index c9054fd..9fee7bb 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -32,7 +32,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -40,6 +39,7 @@
 import android.content.pm.SELinuxUtil;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ComponentParseUtils;
@@ -273,16 +273,16 @@
                 pi.requestedPermissionsFlags = new int[size];
                 for (int i = 0; i < size; i++) {
                     final ParsedUsesPermission usesPermission = usesPermissions.get(i);
-                    pi.requestedPermissions[i] = usesPermission.name;
+                    pi.requestedPermissions[i] = usesPermission.getName();
                     // The notion of required permissions is deprecated but for compatibility.
                     pi.requestedPermissionsFlags[i] |=
                             PackageInfo.REQUESTED_PERMISSION_REQUIRED;
                     if (grantedPermissions != null
-                            && grantedPermissions.contains(usesPermission.name)) {
+                            && grantedPermissions.contains(usesPermission.getName())) {
                         pi.requestedPermissionsFlags[i] |=
                                 PackageInfo.REQUESTED_PERMISSION_GRANTED;
                     }
-                    if ((usesPermission.usesPermissionFlags
+                    if ((usesPermission.getUsesPermissionFlags()
                             & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
                         pi.requestedPermissionsFlags[i] |=
                                 PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
@@ -330,26 +330,26 @@
             pi.isApex = true;
         }
 
-        PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+        final SigningDetails signingDetails = pkg.getSigningDetails();
         // deprecated method of getting signing certificates
         if ((flags & PackageManager.GET_SIGNATURES) != 0) {
             if (signingDetails.hasPastSigningCertificates()) {
                 // Package has included signing certificate rotation information.  Return the oldest
                 // cert so that programmatic checks keep working even if unaware of key rotation.
                 pi.signatures = new Signature[1];
-                pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+                pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
             } else if (signingDetails.hasSignatures()) {
                 // otherwise keep old behavior
-                int numberOfSigs = signingDetails.signatures.length;
+                int numberOfSigs = signingDetails.getSignatures().length;
                 pi.signatures = new Signature[numberOfSigs];
-                System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+                System.arraycopy(signingDetails.getSignatures(), 0, pi.signatures, 0,
                         numberOfSigs);
             }
         }
 
         // replacement for GET_SIGNATURES
         if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
-            if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+            if (signingDetails != SigningDetails.UNKNOWN) {
                 // only return a valid SigningInfo if there is signing information to report
                 pi.signingInfo = new SigningInfo(signingDetails);
             } else {
@@ -398,6 +398,13 @@
             assignUserFields(pkg, ai, userId);
         }
 
+        updateApplicationInfo(ai, flags, state);
+
+        return ai;
+    }
+
+    private static void updateApplicationInfo(ApplicationInfo ai, int flags,
+            PackageUserState state) {
         if ((flags & PackageManager.GET_META_DATA) == 0) {
             ai.metaData = null;
         }
@@ -407,7 +414,7 @@
         }
 
         // CompatibilityMode is global state.
-        if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) {
+        if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
             ai.disableCompatibilityMode();
         }
 
@@ -439,7 +446,37 @@
             ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
             ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
         }
+    }
 
+    @Nullable
+    public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
+            @PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
+            int userId) {
+        if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
+            return null;
+        }
+        // This is used to return the ResolverActivity or instantAppInstallerActivity;
+        // we will just always make a copy.
+        ai = new ApplicationInfo(ai);
+        ai.initForUser(userId);
+        ai.icon = (ParsingPackageUtils.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes
+                : ai.iconRes;
+        updateApplicationInfo(ai, flags, state);
+        return ai;
+    }
+
+    @Nullable
+    public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
+            @PackageManager.ComponentInfoFlags int flags, @NonNull PackageUserState state,
+            int userId) {
+        if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
+            return null;
+        }
+        // This is used to return the ResolverActivity or instantAppInstallerActivity;
+        // we will just always make a copy.
+        final ActivityInfo ai = new ActivityInfo(a);
+        ai.applicationInfo =
+                generateDelegateApplicationInfo(ai.applicationInfo, flags, state, userId);
         return ai;
     }
 
@@ -713,7 +750,24 @@
     @Nullable
     public static Attribution generateAttribution(ParsedAttribution pa) {
         if (pa == null) return null;
-        return new Attribution(pa.tag, pa.label);
+        return new Attribution(pa.getTag(), pa.getLabel());
+    }
+
+    private static boolean checkUseInstalledOrHidden(int flags, @NonNull PackageUserState state,
+            @Nullable ApplicationInfo appInfo) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.installed
+                && appInfo != null && appInfo.hiddenUntilInstalled) {
+            return false;
+        }
+
+        // If available for the target user, or trying to match uninstalled packages and it's
+        // a system app.
+        return state.isAvailable(flags)
+                || (appInfo != null && appInfo.isSystemApp()
+                && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
     }
 
     private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index ed68dbf..72cc929 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -25,7 +25,7 @@
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -320,7 +320,7 @@
 
     ParsingPackage setSharedUserLabel(int sharedUserLabel);
 
-    ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+    ParsingPackage setSigningDetails(SigningDetails signingDetails);
 
     ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 5a7f210..0db6546 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -32,7 +32,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -292,7 +292,7 @@
     @DataClass.ParcelWith(ForInternedString.class)
     protected String volumeUuid;
     @Nullable
-    private PackageParser.SigningDetails signingDetails;
+    private SigningDetails signingDetails;
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
@@ -716,13 +716,15 @@
 
         // Continue populating legacy data structures to avoid performance
         // issues until all that code can be migrated
-        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions, permission.name);
+        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
+                permission.getName());
 
         return this;
     }
 
     @Override
     public ParsingPackageImpl addImplicitPermission(String permission) {
+        addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
         this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
                 TextUtils.safeIntern(permission));
         return this;
@@ -1714,7 +1716,7 @@
 
     @Nullable
     @Override
-    public PackageParser.SigningDetails getSigningDetails() {
+    public SigningDetails getSigningDetails() {
         return signingDetails;
     }
 
@@ -2276,7 +2278,7 @@
     }
 
     @Override
-    public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+    public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
         signingDetails = value;
         return this;
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a6e189d..d5bd3a9 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -26,8 +26,8 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -768,7 +768,7 @@
      * The signature data of all APKs in this package, which must be exactly the same across the
      * base and splits.
      */
-    PackageParser.SigningDetails getSigningDetails();
+    SigningDetails getSigningDetails();
 
     /**
      * @see ApplicationInfo#splitClassLoaderNames
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index dce242c..fc4aa5e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -18,8 +18,10 @@
 
 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
@@ -46,9 +48,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityUtils;
@@ -74,6 +75,7 @@
 import android.content.pm.parsing.result.ParseInput.DeferredError;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.CompatibilityPermissionInfo;
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
@@ -273,31 +275,25 @@
                                 manifestArray);
                     }
                 });
-        try {
-            result = parser.parsePackage(input, file, parseFlags);
-            if (result.isError()) {
-                return result;
-            }
-        } catch (PackageParser.PackageParserException e) {
-            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Error parsing package", e);
+        result = parser.parsePackage(input, file, parseFlags);
+        if (result.isError()) {
+            return input.error(result);
         }
 
-        try {
-            ParsingPackage pkg = result.getResult();
-            if (collectCertificates) {
-                pkg.setSigningDetails(
-                        ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
+        final ParsingPackage pkg = result.getResult();
+        if (collectCertificates) {
+            final ParseResult<SigningDetails> ret =
+                    ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
+            if (ret.isError()) {
+                return input.error(ret);
             }
-
-            // Need to call this to finish the parsing stage
-            pkg.hideAsParsed();
-
-            return input.success(pkg);
-        } catch (PackageParser.PackageParserException e) {
-            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Error collecting package certificates", e);
+            pkg.setSigningDetails(ret.getResult());
         }
+
+        // Need to call this to finish the parsing stage
+        pkg.hideAsParsed();
+
+        return input.success(pkg);
     }
 
     private boolean mOnlyCoreApps;
@@ -327,8 +323,8 @@
      * requiring identical package name and version codes, a single base APK,
      * and unique split names.
      * <p>
-     * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+     * Note that this <em>does not</em> perform signature verification; that must
+     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
      *
      * If {@code useCaches} is true, the package parser might return a cached
      * result from a previous parse of the same {@code packageFile} with the same
@@ -337,9 +333,7 @@
      *
      * @see PackageParser#parsePackageLite(File, int)
      */
-    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
-            int flags)
-            throws PackageParserException {
+    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
         if (packageFile.isDirectory()) {
             return parseClusterPackage(input, packageFile, flags);
         } else {
@@ -353,8 +347,8 @@
      * identical package name and version codes, a single base APK, and unique
      * split names.
      * <p>
-     * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+     * Note that this <em>does not</em> perform signature verification; that must
+     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
      */
     private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
             int flags) {
@@ -404,15 +398,19 @@
 
                 for (int i = 0; i < num; i++) {
                     final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
-                    parseSplitApk(input, pkg, i, splitAssets, flags);
+                    final ParseResult<ParsingPackage> split =
+                            parseSplitApk(input, pkg, i, splitAssets, flags);
+                    if (split.isError()) {
+                        return input.error(split);
+                    }
                 }
             }
 
             pkg.setUse32BitAbi(lite.isUse32bitAbi());
             return input.success(pkg);
-        } catch (PackageParserException e) {
-            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Failed to load assets: " + lite.getBaseApkPath(), e);
+        } catch (IllegalArgumentException e) {
+            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
         } finally {
             IoUtils.closeQuietly(assetLoader);
         }
@@ -421,11 +419,11 @@
     /**
      * Parse the given APK file, treating it as as a single monolithic package.
      * <p>
-     * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+     * Note that this <em>does not</em> perform signature verification; that must
+     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
      */
     private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
-            int flags) throws PackageParserException {
+            int flags) {
         final ParseResult<PackageLite> liteResult =
                 ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
         if (liteResult.isError()) {
@@ -459,8 +457,7 @@
     }
 
     private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
-            String codePath, SplitAssetLoader assetLoader, int flags)
-            throws PackageParserException {
+            String codePath, SplitAssetLoader assetLoader, int flags) {
         final String apkPath = apkFile.getAbsolutePath();
 
         String volumeUuid = null;
@@ -471,7 +468,13 @@
 
         if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
 
-        final AssetManager assets = assetLoader.getBaseAssetManager();
+        final AssetManager assets;
+        try {
+            assets = assetLoader.getBaseAssetManager();
+        } catch (IllegalArgumentException e) {
+            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
+        }
         final int cookie = assets.findCookieForPath(apkPath);
         if (cookie == 0) {
             return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -528,7 +531,12 @@
             pkg.setVolumeUuid(volumeUuid);
 
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
-                pkg.setSigningDetails(getSigningDetails(pkg, false));
+                final ParseResult<SigningDetails> ret =
+                        getSigningDetails(input, pkg, false /*skipVerify*/);
+                if (ret.isError()) {
+                    return input.error(ret);
+                }
+                pkg.setSigningDetails(ret.getResult());
             } else {
                 pkg.setSigningDetails(SigningDetails.UNKNOWN);
             }
@@ -634,11 +642,13 @@
      */
     private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
             Resources res, XmlResourceParser parser, int flags, int splitIndex)
-            throws XmlPullParserException, IOException, PackageParserException {
-        AttributeSet attrs = parser;
-
+            throws XmlPullParserException, IOException {
         // We parsed manifest tag earlier; just skip past it
-        PackageParser.parsePackageSplitNames(parser, attrs);
+        final ParseResult<Pair<String, String>> packageSplitResult =
+                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
+        if (packageSplitResult.isError()) {
+            return input.error(packageSplitResult);
+        }
 
         int type;
 
@@ -905,7 +915,7 @@
             );
         }
 
-        convertNewPermissions(pkg);
+        convertCompatPermissions(pkg);
 
         convertSplitPermissions(pkg);
 
@@ -1297,8 +1307,8 @@
             final int size = usesPermissions.size();
             for (int i = 0; i < size; i++) {
                 final ParsedUsesPermission usesPermission = usesPermissions.get(i);
-                if (Objects.equals(usesPermission.name, name)) {
-                    if (usesPermission.usesPermissionFlags != usesPermissionFlags) {
+                if (Objects.equals(usesPermission.getName(), name)) {
+                    if (usesPermission.getUsesPermissionFlags() != usesPermissionFlags) {
                         return input.error("Conflicting uses-permissions flags: "
                                 + name + " in package: " + pkg.getPackageName() + " at: "
                                 + parser.getPositionDescription());
@@ -2791,31 +2801,16 @@
         }
     }
 
-    private static void convertNewPermissions(ParsingPackage pkg) {
-        final int NP = PackageParser.NEW_PERMISSIONS.length;
-        StringBuilder newPermsMsg = null;
-        for (int ip = 0; ip < NP; ip++) {
-            final PackageParser.NewPermissionInfo npi
-                    = PackageParser.NEW_PERMISSIONS[ip];
-            if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+    private static void convertCompatPermissions(ParsingPackage pkg) {
+        for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+            final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+            if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
                 break;
             }
-            if (!pkg.getRequestedPermissions().contains(npi.name)) {
-                if (newPermsMsg == null) {
-                    newPermsMsg = new StringBuilder(128);
-                    newPermsMsg.append(pkg.getPackageName());
-                    newPermsMsg.append(": compat added ");
-                } else {
-                    newPermsMsg.append(' ');
-                }
-                newPermsMsg.append(npi.name);
-                pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
-                        .addImplicitPermission(npi.name);
+            if (!pkg.getRequestedPermissions().contains(info.getName())) {
+                pkg.addImplicitPermission(info.getName());
             }
         }
-        if (newPermsMsg != null) {
-            Slog.i(TAG, newPermsMsg.toString());
-        }
     }
 
     private void convertSplitPermissions(ParsingPackage pkg) {
@@ -2831,8 +2826,7 @@
             for (int in = 0; in < newPerms.size(); in++) {
                 final String perm = newPerms.get(in);
                 if (!requestedPermissions.contains(perm)) {
-                    pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
-                            .addImplicitPermission(perm);
+                    pkg.addImplicitPermission(perm);
                 }
             }
         }
@@ -2976,12 +2970,10 @@
      *  construct a dummy ParseInput.
      */
     @CheckResult
-    public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
-            throws PackageParserException {
+    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+            ParsingPackageRead pkg, boolean skipVerify) {
         SigningDetails signingDetails = SigningDetails.UNKNOWN;
 
-        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
-
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
             ParseResult<SigningDetails> result = getSigningDetails(
@@ -2993,8 +2985,7 @@
                     pkg.getTargetSdkVersion()
             );
             if (result.isError()) {
-                throw new PackageParser.PackageParserException(result.getErrorCode(),
-                        result.getErrorMessage(), result.getException());
+                return input.error(result);
             }
 
             signingDetails = result.getResult();
@@ -3011,15 +3002,11 @@
                             pkg.getTargetSdkVersion()
                     );
                     if (result.isError()) {
-                        throw new PackageParser.PackageParserException(result.getErrorCode(),
-                                result.getErrorMessage(), result.getException());
+                        return input.error(result);
                     }
-
-
-                    signingDetails = result.getResult();
                 }
             }
-            return signingDetails;
+            return result;
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -3035,29 +3022,29 @@
             // must use v2 signing scheme
             minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
         }
-        SigningDetails verified;
-        try {
-            if (skipVerify) {
-                // systemDir APKs are already trusted, save time by not verifying; since the
-                // signature is not verified and some system apps can have their V2+ signatures
-                // stripped allow pulling the certs from the jar signature.
-                verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
-                        baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
-            } else {
-                verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
-            }
-        } catch (PackageParserException e) {
-            return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "Failed collecting certificates for " + baseCodePath, e);
+        final ParseResult<SigningDetails> verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying; since the
+            // signature is not verified and some system apps can have their V2+ signatures
+            // stripped allow pulling the certs from the jar signature.
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
+                    SigningDetails.SignatureSchemeVersion.JAR);
+        } else {
+            verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
+        }
+
+        if (verified.isError()) {
+            return input.error(verified);
         }
 
         // Verify that entries are signed consistently with the first pkg
         // we encountered. Note that for splits, certificates may have
         // already been populated during an earlier parse of a base APK.
         if (existingSigningDetails == SigningDetails.UNKNOWN) {
-            return input.success(verified);
+            return verified;
         } else {
-            if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+            if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+                    verified.getResult().getSignatures())) {
                 return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                         baseCodePath + " has mismatched certificates");
             }
@@ -3069,6 +3056,13 @@
     /**
      * @hide
      */
+    public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
+        sCompatibilityModeEnabled = compatibilityModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
     public static void readConfigUseRoundIcon(Resources r) {
         if (r != null) {
             sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index 0403a25..e5d030c 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -152,7 +152,7 @@
     @Nullable
     public static CharSequence getNonLocalizedLabel(
             ParsedComponent component) {
-        return component.nonLocalizedLabel;
+        return component.getNonLocalizedLabel();
     }
 
     /**
@@ -161,7 +161,7 @@
      * This is a method of the utility class to discourage use.
      */
     public static int getIcon(ParsedComponent component) {
-        return component.icon;
+        return component.getIcon();
     }
 
     public static boolean isMatch(PackageUserState state, boolean isSystem,
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 6f478ac..73ee132 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -38,8 +38,8 @@
 /** @hide **/
 public class ParsedActivity extends ParsedMainComponent {
 
-    int theme;
-    int uiOptions;
+    private int theme;
+    private int uiOptions;
 
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
@@ -49,22 +49,22 @@
     @DataClass.ParcelWith(ForInternedString.class)
     private String parentActivityName;
     @Nullable
-    String taskAffinity;
-    int privateFlags;
+    private String taskAffinity;
+    private int privateFlags;
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String permission;
 
-    int launchMode;
-    int documentLaunchMode;
-    int maxRecents;
-    int configChanges;
-    int softInputMode;
-    int persistableMode;
-    int lockTaskLaunchMode;
+    private int launchMode;
+    private int documentLaunchMode;
+    private int maxRecents;
+    private int configChanges;
+    private int softInputMode;
+    private int persistableMode;
+    private int lockTaskLaunchMode;
 
-    int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-    int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+    private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
 
     @Nullable
     private Float maxAspectRatio;
@@ -75,12 +75,12 @@
     private boolean supportsSizeChanges;
 
     @Nullable
-    String requestedVrComponent;
-    int rotationAnimation = -1;
-    int colorMode;
+    private String requestedVrComponent;
+    private int rotationAnimation = -1;
+    private int colorMode;
 
     @Nullable
-    ActivityInfo.WindowLayout windowLayout;
+    private ActivityInfo.WindowLayout windowLayout;
 
     public ParsedActivity(ParsedActivity other) {
         super(other);
@@ -188,6 +188,35 @@
 //        alias.metaData = target.metaData;
     }
 
+    public boolean isSupportsSizeChanges() {
+        return supportsSizeChanges;
+    }
+
+    public ParsedActivity setColorMode(int colorMode) {
+        this.colorMode = colorMode;
+        return this;
+    }
+
+    public ParsedActivity setConfigChanges(int configChanges) {
+        this.configChanges = configChanges;
+        return this;
+    }
+
+    public ParsedActivity setDocumentLaunchMode(int documentLaunchMode) {
+        this.documentLaunchMode = documentLaunchMode;
+        return this;
+    }
+
+    public ParsedActivity setLaunchMode(int launchMode) {
+        this.launchMode = launchMode;
+        return this;
+    }
+
+    public ParsedActivity setLockTaskLaunchMode(int lockTaskLaunchMode) {
+        this.lockTaskLaunchMode = lockTaskLaunchMode;
+        return this;
+    }
+
     public ParsedActivity setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
         if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
                 || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
@@ -204,6 +233,16 @@
         return this;
     }
 
+    public ParsedActivity setMaxAspectRatio(Float maxAspectRatio) {
+        this.maxAspectRatio = maxAspectRatio;
+        return this;
+    }
+
+    public ParsedActivity setMaxRecents(int maxRecents) {
+        this.maxRecents = maxRecents;
+        return this;
+    }
+
     public ParsedActivity setMinAspectRatio(int resizeMode, float minAspectRatio) {
         if (resizeMode == RESIZE_MODE_RESIZEABLE
                 || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
@@ -220,13 +259,48 @@
         return this;
     }
 
-    public ParsedActivity setSupportsSizeChanges(boolean supportsSizeChanges) {
-        this.supportsSizeChanges = supportsSizeChanges;
+    public ParsedActivity setMinAspectRatio(Float minAspectRatio) {
+        this.minAspectRatio = minAspectRatio;
         return this;
     }
 
-    public ParsedActivity setFlags(int flags) {
-        this.flags = flags;
+    public ParsedActivity setParentActivityName(String parentActivityName) {
+        this.parentActivityName = parentActivityName;
+        return this;
+    }
+
+    public ParsedActivity setPersistableMode(int persistableMode) {
+        this.persistableMode = persistableMode;
+        return this;
+    }
+
+    public ParsedActivity setPrivateFlags(int privateFlags) {
+        this.privateFlags = privateFlags;
+        return this;
+    }
+
+    public ParsedActivity setRequestedVrComponent(String requestedVrComponent) {
+        this.requestedVrComponent = requestedVrComponent;
+        return this;
+    }
+
+    public ParsedActivity setRotationAnimation(int rotationAnimation) {
+        this.rotationAnimation = rotationAnimation;
+        return this;
+    }
+
+    public ParsedActivity setScreenOrientation(int screenOrientation) {
+        this.screenOrientation = screenOrientation;
+        return this;
+    }
+
+    public ParsedActivity setSoftInputMode(int softInputMode) {
+        this.softInputMode = softInputMode;
+        return this;
+    }
+
+    public ParsedActivity setSupportsSizeChanges(boolean supportsSizeChanges) {
+        this.supportsSizeChanges = supportsSizeChanges;
         return this;
     }
 
@@ -240,17 +314,32 @@
         return this;
     }
 
-    public ParsedActivity setParentActivity(String parentActivity) {
-        this.parentActivityName = TextUtils.safeIntern(parentActivity);
-        return this;
-    }
-
     public ParsedActivity setPermission(String permission) {
         // Empty string must be converted to null
         this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
         return this;
     }
 
+    public ParsedActivity setTaskAffinity(String taskAffinity) {
+        this.taskAffinity = taskAffinity;
+        return this;
+    }
+
+    public ParsedActivity setTheme(int theme) {
+        this.theme = theme;
+        return this;
+    }
+
+    public ParsedActivity setUiOptions(int uiOptions) {
+        this.uiOptions = uiOptions;
+        return this;
+    }
+
+    public ParsedActivity setWindowLayout(ActivityInfo.WindowLayout windowLayout) {
+        this.windowLayout = windowLayout;
+        return this;
+    }
+
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("Activity{");
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 92a90e9..ac6bcd0 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -120,58 +120,58 @@
             // Multi-lining them to fit within the column restriction makes it hard to tell what
             // field is assigned where.
             // @formatter:off
-            activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
-            activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions());
+            activity.setTheme(sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0))
+                    .setUiOptions(sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions()));
 
-            activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
-                    | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
-                    | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
-                    | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
-                    | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
-                    | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
-                    | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
-                    | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
-                    | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
-                    | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
-                    | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
-                    | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
-                    | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa);
+            activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
+                                | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
+                                | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
+                                | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
+                                | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
+                                | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
+                                | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
+                                | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
+                                | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
+                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
+                                | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
+                                | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
+                                | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa)));
 
             if (!receiver) {
-                activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
-                        | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
-                        | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
-                        | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
-                        | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
-                        | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
-                        | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
-                        | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
-                        | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
-                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa);
+                activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
+                                        | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
+                                        | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
+                                        | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
+                                        | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
+                                        | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
+                                        | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
+                                        | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
+                                        | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
+                                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
 
-                activity.privateFlags |= flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
-                        R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
-                        | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
-                        R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa);
+                activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
+                                        R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
+                                        | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
+                                        R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa)));
 
-                activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);
-                activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE);
-                activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
-                activity.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
-                activity.maxRecents = sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic());
-                activity.persistableMode = sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY);
-                activity.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
-                activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
-                activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
-
-                activity.configChanges = getActivityConfigChanges(
-                        sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
-                        sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+                activity.setColorMode(sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT))
+                        .setDocumentLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE))
+                        .setLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE))
+                        .setLockTaskLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0))
+                        .setMaxRecents(sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic()))
+                        .setPersistableMode(sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY))
+                        .setRequestedVrComponent(sa.getString(R.styleable.AndroidManifestActivity_enableVrMode))
+                        .setRotationAnimation(sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED))
+                        .setSoftInputMode(sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0))
+                        .setConfigChanges(getActivityConfigChanges(
+                                sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                                sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0))
+                        );
 
                 int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
                 int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
-                activity.screenOrientation = screenOrientation;
-                activity.resizeMode = resizeMode;
+                activity.setScreenOrientation(screenOrientation)
+                        .setResizeMode(resizeMode);
 
                 if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
                         && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
@@ -189,9 +189,9 @@
                                     0 /*default*/));
                 }
             } else {
-                activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-                activity.configChanges = 0;
-                activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
+                activity.setLaunchMode(ActivityInfo.LAUNCH_MULTIPLE)
+                        .setConfigChanges(0)
+                        .setFlags(activity.getFlags()|flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa));
             }
             // @formatter:on
 
@@ -205,11 +205,11 @@
                 return input.error(affinityNameResult);
             }
 
-            activity.taskAffinity = affinityNameResult.getResult();
+            activity.setTaskAffinity(affinityNameResult.getResult());
 
             boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
             if (visibleToEphemeral) {
-                activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
                 pkg.setVisibleToInstantApps(true);
             }
 
@@ -320,7 +320,7 @@
                 Log.e(TAG, "Activity " + activity.getName()
                         + " specified invalid parentActivityName " + parentActivityName);
             } else {
-                activity.setParentActivity(parentClassName);
+                activity.setParentActivityName(parentClassName);
             }
         }
 
@@ -336,7 +336,7 @@
 
         final boolean setExported = array.hasValue(exportedAttr);
         if (setExported) {
-            activity.exported = array.getBoolean(exportedAttr, false);
+            activity.setExported(array.getBoolean(exportedAttr, false));
         }
 
         final int depth = parser.getDepth();
@@ -355,7 +355,7 @@
                 if (intentResult.isSuccess()) {
                     ParsedIntentInfo intent = intentResult.getResult();
                     if (intent != null) {
-                        activity.order = Math.max(intent.getOrder(), activity.order);
+                        activity.setOrder(Math.max(intent.getOrder(), activity.getOrder()));
                         activity.addIntent(intent);
                         if (LOG_UNSAFE_BROADCASTS && isReceiver
                                 && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
@@ -396,7 +396,7 @@
                 ParseResult<ActivityInfo.WindowLayout> layoutResult =
                         parseActivityWindowLayout(resources, parser, input);
                 if (layoutResult.isSuccess()) {
-                    activity.windowLayout = layoutResult.getResult();
+                    activity.setWindowLayout(layoutResult.getResult());
                 }
                 result = layoutResult;
             } else {
@@ -408,13 +408,13 @@
             }
         }
 
-        if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK
-                && activity.metaData != null && activity.metaData.containsKey(
+        if (!isAlias && activity.getLaunchMode() != LAUNCH_SINGLE_INSTANCE_PER_TASK
+                && activity.getMetaData() != null && activity.getMetaData().containsKey(
                 ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
-            final String launchMode = activity.metaData.getString(
+            final String launchMode = activity.getMetaData().getString(
                     ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
             if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
-                activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;
+                activity.setLaunchMode(LAUNCH_SINGLE_INSTANCE_PER_TASK);
             }
         }
 
@@ -423,7 +423,7 @@
         if (layoutResult.isError()) {
             return input.error(layoutResult);
         }
-        activity.windowLayout = layoutResult.getResult();
+        activity.setWindowLayout(layoutResult.getResult());
 
         if (!setExported) {
             boolean hasIntentFilters = activity.getIntents().size() > 0;
@@ -437,7 +437,7 @@
                     return input.error(exportedCheckResult);
                 }
             }
-            activity.exported = hasIntentFilters;
+            activity.setExported(hasIntentFilters);
         }
 
         return input.success(activity);
@@ -459,10 +459,11 @@
         ParsedIntentInfo intent = result.getResult();
         if (intent != null) {
             if (intent.isVisibleToInstantApp()) {
-                activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
             }
             if (intent.isImplicitlyVisibleToInstantApp()) {
-                activity.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                activity.setFlags(
+                        activity.getFlags() | ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP);
             }
         }
 
@@ -553,19 +554,19 @@
     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(
+        if (activity.getMetaData() == null || !activity.getMetaData().containsKey(
                 ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
-            return input.success(activity.windowLayout);
+            return input.success(activity.getWindowLayout());
         }
 
         // Layout already specifies a value. We should just use that one.
-        if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
-            return input.success(activity.windowLayout);
+        if (activity.getWindowLayout() != null && activity.getWindowLayout().windowLayoutAffinity != null) {
+            return input.success(activity.getWindowLayout());
         }
 
-        String windowLayoutAffinity = activity.metaData.getString(
+        String windowLayoutAffinity = activity.getMetaData().getString(
                 ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
-        ActivityInfo.WindowLayout layout = activity.windowLayout;
+        ActivityInfo.WindowLayout layout = activity.getWindowLayout();
         if (layout == null) {
             layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
                     -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index 4ec2e73..db3a1c4 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -34,7 +34,7 @@
  *
  * @hide
  */
-@DataClass(genAidl = false)
+@DataClass(genAidl = false, genSetters = true, genBuilder = false)
 public class ParsedAttribution implements Parcelable {
     /** Maximum length of attribution tag */
     public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
@@ -43,13 +43,15 @@
     private static final int MAX_NUM_ATTRIBUTIONS = 10000;
 
     /** Tag of the attribution */
-    public final @NonNull String tag;
+    private @NonNull String tag;
 
     /** User visible label fo the attribution */
-    public final @StringRes int label;
+    private @StringRes int label;
 
     /** Ids of previously declared attributions this attribution inherits from */
-    public final @NonNull List<String> inheritFrom;
+    private @NonNull List<String> inheritFrom;
+
+    public ParsedAttribution() {}
 
     /**
      * @return Is this set of attributions a valid combination for a single package?
@@ -160,6 +162,63 @@
         // onConstructed(); // You can define this method to get a callback
     }
 
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTag() {
+        return tag;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @StringRes int getLabel() {
+        return label;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getInheritFrom() {
+        return inheritFrom;
+    }
+
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttribution setTag(@NonNull String value) {
+        tag = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        return this;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttribution setLabel(@StringRes int value) {
+        label = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        return this;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttribution setInheritFrom(@NonNull List<String> value) {
+        inheritFrom = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -215,10 +274,10 @@
     };
 
     @DataClass.Generated(
-            time = 1618351459610L,
+            time = 1624050667337L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
-            inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
+            inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 9d830ec..3c0f097 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -48,33 +48,33 @@
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
-    private String name;
-    int icon;
-    int labelRes;
+    protected String name;
+    protected int icon;
+    protected int labelRes;
     @Nullable
-    CharSequence nonLocalizedLabel;
-    int logo;
-    int banner;
-    int descriptionRes;
+    protected CharSequence nonLocalizedLabel;
+    protected int logo;
+    protected int banner;
+    protected int descriptionRes;
 
     // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
-    int flags;
+    protected int flags;
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
-    private String packageName;
+    protected String packageName;
 
     @Nullable
     @DataClass.PluralOf("intent")
     @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
-    private List<ParsedIntentInfo> intents;
+    protected List<ParsedIntentInfo> intents;
 
-    private ComponentName componentName;
+    protected ComponentName componentName;
 
     @Nullable
     protected Bundle metaData;
 
-    private Map<String, Property> mProperties = emptyMap();
+    protected Map<String, Property> mProperties = emptyMap();
 
     ParsedComponent() {
 
@@ -112,11 +112,51 @@
         return intents != null ? intents : Collections.emptyList();
     }
 
+    public ParsedComponent setBanner(int banner) {
+        this.banner = banner;
+        return this;
+    }
+
+    public ParsedComponent setDescriptionRes(int descriptionRes) {
+        this.descriptionRes = descriptionRes;
+        return this;
+    }
+
+    public ParsedComponent setFlags(int flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    public ParsedComponent setIcon(int icon) {
+        this.icon = icon;
+        return this;
+    }
+
+    public ParsedComponent setLabelRes(int labelRes) {
+        this.labelRes = labelRes;
+        return this;
+    }
+
+    public ParsedComponent setLogo(int logo) {
+        this.logo = logo;
+        return this;
+    }
+
+    public ParsedComponent setMetaData(Bundle metaData) {
+        this.metaData = metaData;
+        return this;
+    }
+
     public ParsedComponent setName(String name) {
         this.name = TextUtils.safeIntern(name);
         return this;
     }
 
+    public ParsedComponent setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
+        this.nonLocalizedLabel = nonLocalizedLabel;
+        return this;
+    }
+
     @CallSuper
     public void setPackageName(@NonNull String packageName) {
         this.packageName = TextUtils.safeIntern(packageName);
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index 46b9419..ab596d3 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -56,40 +56,40 @@
         }
 
         //noinspection ConstantConditions; null check done above with isEmpty
-        component.setName(className);
-        component.setPackageName(packageName);
+        component.setName(className)
+                .setPackageName(packageName);
 
         int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
         if (roundIconVal != 0) {
-            component.icon = roundIconVal;
-            component.nonLocalizedLabel = null;
+            component.setIcon(roundIconVal)
+                    .setNonLocalizedLabel(null);
         } else {
             int iconVal = array.getResourceId(iconAttr, 0);
             if (iconVal != 0) {
-                component.icon = iconVal;
-                component.nonLocalizedLabel = null;
+                component.setIcon(iconVal);
+                component.setNonLocalizedLabel(null);
             }
         }
 
         int logoVal = array.getResourceId(logoAttr, 0);
         if (logoVal != 0) {
-            component.logo = logoVal;
+            component.setLogo(logoVal);
         }
 
         int bannerVal = array.getResourceId(bannerAttr, 0);
         if (bannerVal != 0) {
-            component.banner = bannerVal;
+            component.setBanner(bannerVal);
         }
 
         if (descriptionAttr != null) {
-            component.descriptionRes = array.getResourceId(descriptionAttr, 0);
+            component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
         }
 
         TypedValue v = array.peekValue(labelAttr);
         if (v != null) {
-            component.labelRes = v.resourceId;
+            component.setLabelRes(v.resourceId);
             if (v.resourceId == 0) {
-                component.nonLocalizedLabel = v.coerceToString();
+                component.setNonLocalizedLabel(v.coerceToString());
             }
         }
 
@@ -105,9 +105,9 @@
         }
         final Property property = result.getResult();
         if (property != null) {
-            component.metaData = property.toBundle(component.metaData);
+            component.setMetaData(property.toBundle(component.getMetaData()));
         }
-        return input.success(component.metaData);
+        return input.success(component.getMetaData());
     }
 
     static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg,
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
index aa33e79..65ff472 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
@@ -36,18 +36,30 @@
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String targetProcesses;
-    boolean handleProfiling;
-    boolean functionalTest;
+    private boolean handleProfiling;
+    private boolean functionalTest;
 
     public ParsedInstrumentation() {
     }
 
-    public void setTargetPackage(@Nullable String targetPackage) {
-        this.targetPackage = TextUtils.safeIntern(targetPackage);
+    public ParsedInstrumentation setFunctionalTest(boolean functionalTest) {
+        this.functionalTest = functionalTest;
+        return this;
     }
 
-    public void setTargetProcesses(@Nullable String targetProcesses) {
+    public ParsedInstrumentation setHandleProfiling(boolean handleProfiling) {
+        this.handleProfiling = handleProfiling;
+        return this;
+    }
+
+    public ParsedInstrumentation setTargetPackage(@Nullable String targetPackage) {
+        this.targetPackage = TextUtils.safeIntern(targetPackage);
+        return this;
+    }
+
+    public ParsedInstrumentation setTargetProcesses(@Nullable String targetProcesses) {
         this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+        return this;
     }
 
     public String toString() {
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
index 89645fc..5977c83 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -59,10 +59,10 @@
             // @formatter:off
             // Note: don't allow this value to be a reference to a resource
             // that may change.
-            instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage));
-            instrumentation.setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses));
-            instrumentation.handleProfiling = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
-            instrumentation.functionalTest = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false);
+            instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage))
+                    .setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses))
+                    .setHandleProfiling(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false))
+                    .setFunctionalTest(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false));
             // @formatter:on
         } finally {
             sa.recycle();
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 463a181..01ee0f4 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -31,6 +31,26 @@
 
     public static final Parceler PARCELER = new Parceler();
 
+    public ParsedIntentInfo setHasDefault(boolean hasDefault) {
+        this.hasDefault = hasDefault;
+        return this;
+    }
+
+    public ParsedIntentInfo setIcon(int icon) {
+        this.icon = icon;
+        return this;
+    }
+
+    public ParsedIntentInfo setLabelRes(int labelRes) {
+        this.labelRes = labelRes;
+        return this;
+    }
+
+    public ParsedIntentInfo setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
+        this.nonLocalizedLabel = nonLocalizedLabel;
+        return this;
+    }
+
     public static class Parceler implements Parcelling<ParsedIntentInfo> {
 
         @Override
@@ -135,11 +155,11 @@
         }
     }
 
-    boolean hasDefault;
-    int labelRes;
+    private boolean hasDefault;
+    private int labelRes;
     @Nullable
-    CharSequence nonLocalizedLabel;
-    int icon;
+    private CharSequence nonLocalizedLabel;
+    private int icon;
 
     public ParsedIntentInfo() {
     }
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 939e77f..dd71fa4 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -60,19 +60,20 @@
 
             TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
             if (v != null) {
-                intentInfo.labelRes = v.resourceId;
+                intentInfo.setLabelRes(v.resourceId);
                 if (v.resourceId == 0) {
-                    intentInfo.nonLocalizedLabel = v.coerceToString();
+                    intentInfo.setNonLocalizedLabel(v.coerceToString());
                 }
             }
 
             if (ParsingPackageUtils.sUseRoundIcon) {
-                intentInfo.icon = sa.getResourceId(
-                        R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
+                intentInfo.setIcon(sa.getResourceId(
+                        R.styleable.AndroidManifestIntentFilter_roundIcon, 0));
             }
 
-            if (intentInfo.icon == 0) {
-                intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
+            if (intentInfo.getIcon() == 0) {
+                intentInfo.setIcon(
+                        sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0));
             }
 
             if (allowAutoVerify) {
@@ -140,7 +141,7 @@
             }
         }
 
-        intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+        intentInfo.setHasDefault(intentInfo.hasCategory(Intent.CATEGORY_DEFAULT));
 
         if (DEBUG) {
             final StringBuilder cats = new StringBuilder("Intent d=");
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
index 033e30f..433bfd3 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -31,16 +31,16 @@
 
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
-    private String processName;
-    boolean directBootAware;
-    boolean enabled = true;
-    boolean exported;
-    int order;
+    protected String processName;
+    protected boolean directBootAware;
+    protected boolean enabled = true;
+    protected boolean exported;
+    protected int order;
 
     @Nullable
-    String splitName;
+    protected String splitName;
     @Nullable
-    String[] attributionTags;
+    protected String[] attributionTags;
 
     public ParsedMainComponent() {
     }
@@ -56,6 +56,11 @@
         this.attributionTags = other.attributionTags;
     }
 
+    public ParsedMainComponent setOrder(int order) {
+        this.order = order;
+        return this;
+    }
+
     public ParsedMainComponent setProcessName(String processName) {
         this.processName = TextUtils.safeIntern(processName);
         return this;
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index 54bcbdd..7ccca93 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -58,14 +58,14 @@
         }
 
         if (directBootAwareAttr != null) {
-            component.directBootAware = array.getBoolean(directBootAwareAttr, false);
+            component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
             if (component.isDirectBootAware()) {
                 pkg.setPartiallyDirectBootAware(true);
             }
         }
 
         if (enabledAttr != null) {
-            component.enabled = array.getBoolean(enabledAttr, true);
+            component.setEnabled(array.getBoolean(enabledAttr, true));
         }
 
         if (processAttr != null) {
@@ -92,13 +92,13 @@
         }
 
         if (splitNameAttr != null) {
-            component.splitName = array.getNonConfigurationString(splitNameAttr, 0);
+            component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
         }
 
         if (attributionTagsAttr != null) {
             final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
             if (attributionTags != null) {
-                component.attributionTags = attributionTags.split("\\|");
+                component.setAttributionTags(attributionTags.split("\\|"));
             }
         }
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index 37e0e87..50bc3d9 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -38,17 +38,17 @@
     private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
 
     @Nullable
-    String backgroundPermission;
+    private String backgroundPermission;
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String group;
-    int requestRes;
-    int protectionLevel;
-    boolean tree;
+    private int requestRes;
+    private int protectionLevel;
+    private boolean tree;
     @Nullable
     private ParsedPermissionGroup parsedPermissionGroup;
     @Nullable
-    Set<String> knownCerts;
+    private Set<String> knownCerts;
 
     @VisibleForTesting
     public ParsedPermission() {
@@ -64,13 +64,13 @@
         this.parsedPermissionGroup = other.parsedPermissionGroup;
     }
 
-    public ParsedPermission setGroup(String group) {
-        this.group = TextUtils.safeIntern(group);
+    public ParsedPermission setBackgroundPermission(String backgroundPermission) {
+        this.backgroundPermission = backgroundPermission;
         return this;
     }
 
-    public ParsedPermission setFlags(int flags) {
-        this.flags = flags;
+    public ParsedPermission setGroup(String group) {
+        this.group = TextUtils.safeIntern(group);
         return this;
     }
 
@@ -116,6 +116,21 @@
         return size;
     }
 
+    public ParsedPermission setKnownCerts(Set<String> knownCerts) {
+        this.knownCerts = knownCerts;
+        return this;
+    }
+
+    public ParsedPermission setRequestRes(int requestRes) {
+        this.requestRes = requestRes;
+        return this;
+    }
+
+    public ParsedPermission setTree(boolean tree) {
+        this.tree = tree;
+        return this;
+    }
+
     public String toString() {
         return "Permission{"
                 + Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
index 741c00c..9fb95c4 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
@@ -22,17 +22,15 @@
 import com.android.internal.util.DataClass;
 
 /** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
 public class ParsedPermissionGroup extends ParsedComponent {
 
-    int requestDetailResourceId;
-    int backgroundRequestResourceId;
-    int backgroundRequestDetailResourceId;
-    int requestRes;
-    int priority;
-
-    public void setPriority(int priority) {
-        this.priority = priority;
-    }
+    private int requestDetailResourceId;
+    private int backgroundRequestResourceId;
+    private int backgroundRequestDetailResourceId;
+    private int requestRes;
+    private int priority;
 
     public String toString() {
         return "PermissionGroup{"
@@ -40,63 +38,162 @@
                 + " " + getName() + "}";
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(this.requestDetailResourceId);
-        dest.writeInt(this.backgroundRequestResourceId);
-        dest.writeInt(this.backgroundRequestDetailResourceId);
-        dest.writeInt(this.requestRes);
-        dest.writeInt(this.priority);
-    }
-
     public ParsedPermissionGroup() {
     }
 
-    protected ParsedPermissionGroup(Parcel in) {
-        super(in);
-        this.requestDetailResourceId = in.readInt();
-        this.backgroundRequestResourceId = in.readInt();
-        this.backgroundRequestDetailResourceId = in.readInt();
-        this.requestRes = in.readInt();
-        this.priority = in.readInt();
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedPermissionGroup(
+            int requestDetailResourceId,
+            int backgroundRequestResourceId,
+            int backgroundRequestDetailResourceId,
+            int requestRes,
+            int priority) {
+        this.requestDetailResourceId = requestDetailResourceId;
+        this.backgroundRequestResourceId = backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+        this.requestRes = requestRes;
+        this.priority = priority;
+
+        // onConstructed(); // You can define this method to get a callback
     }
 
-    public static final Parcelable.Creator<ParsedPermissionGroup> CREATOR =
-            new Parcelable.Creator<ParsedPermissionGroup>() {
-                @Override
-                public ParsedPermissionGroup createFromParcel(Parcel source) {
-                    return new ParsedPermissionGroup(source);
-                }
-
-                @Override
-                public ParsedPermissionGroup[] newArray(int size) {
-                    return new ParsedPermissionGroup[size];
-                }
-            };
-
+    @DataClass.Generated.Member
     public int getRequestDetailResourceId() {
         return requestDetailResourceId;
     }
 
+    @DataClass.Generated.Member
     public int getBackgroundRequestResourceId() {
         return backgroundRequestResourceId;
     }
 
+    @DataClass.Generated.Member
     public int getBackgroundRequestDetailResourceId() {
         return backgroundRequestDetailResourceId;
     }
 
+    @DataClass.Generated.Member
     public int getRequestRes() {
         return requestRes;
     }
 
+    @DataClass.Generated.Member
     public int getPriority() {
         return priority;
     }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroup setRequestDetailResourceId( int value) {
+        requestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestResourceId( int value) {
+        backgroundRequestResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestDetailResourceId( int value) {
+        backgroundRequestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroup setRequestRes( int value) {
+        requestRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroup setPriority( int value) {
+        priority = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        super.writeToParcel(dest, flags);
+
+        dest.writeInt(requestDetailResourceId);
+        dest.writeInt(backgroundRequestResourceId);
+        dest.writeInt(backgroundRequestDetailResourceId);
+        dest.writeInt(requestRes);
+        dest.writeInt(priority);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedPermissionGroup(@android.annotation.NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        super(in);
+
+        int _requestDetailResourceId = in.readInt();
+        int _backgroundRequestResourceId = in.readInt();
+        int _backgroundRequestDetailResourceId = in.readInt();
+        int _requestRes = in.readInt();
+        int _priority = in.readInt();
+
+        this.requestDetailResourceId = _requestDetailResourceId;
+        this.backgroundRequestResourceId = _backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
+        this.requestRes = _requestRes;
+        this.priority = _priority;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<ParsedPermissionGroup> CREATOR
+            = new Parcelable.Creator<ParsedPermissionGroup>() {
+        @Override
+        public ParsedPermissionGroup[] newArray(int size) {
+            return new ParsedPermissionGroup[size];
+        }
+
+        @Override
+        public ParsedPermissionGroup createFromParcel(@android.annotation.NonNull Parcel in) {
+            return new ParsedPermissionGroup(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1624052057830L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java",
+            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroup extends android.content.pm.parsing.component.ParsedComponent implements []\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 8afa70e..eec333c 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -66,9 +66,8 @@
             if (sa.hasValue(
                     R.styleable.AndroidManifestPermission_backgroundPermission)) {
                 if ("android".equals(packageName)) {
-                    permission.backgroundPermission = sa.getNonResourceString(
-                            R.styleable
-                                    .AndroidManifestPermission_backgroundPermission);
+                    permission.setBackgroundPermission(sa.getNonResourceString(
+                            R.styleable.AndroidManifestPermission_backgroundPermission));
                 } else {
                     Slog.w(TAG, packageName + " defines a background permission. Only the "
                             + "'android' package can do that.");
@@ -78,17 +77,14 @@
             // Note: don't allow this value to be a reference to a resource
             // that may change.
             permission.setGroup(sa.getNonResourceString(
-                    R.styleable.AndroidManifestPermission_permissionGroup));
-
-            permission.requestRes = sa.getResourceId(
-                    R.styleable.AndroidManifestPermission_request, 0);
-
-            permission.protectionLevel = sa.getInt(
-                    R.styleable.AndroidManifestPermission_protectionLevel,
-                    PermissionInfo.PROTECTION_NORMAL);
-
-            permission.flags = sa.getInt(
-                    R.styleable.AndroidManifestPermission_permissionFlags, 0);
+                    R.styleable.AndroidManifestPermission_permissionGroup))
+                    .setRequestRes(sa.getResourceId(
+                            R.styleable.AndroidManifestPermission_request, 0))
+                    .setProtectionLevel(sa.getInt(
+                            R.styleable.AndroidManifestPermission_protectionLevel,
+                            PermissionInfo.PROTECTION_NORMAL))
+                    .setFlags(sa.getInt(
+                            R.styleable.AndroidManifestPermission_permissionFlags, 0));
 
             final int knownCertsResource = sa.getResourceId(
                     R.styleable.AndroidManifestPermission_knownCerts, 0);
@@ -108,7 +104,7 @@
                         permission.setKnownCert(knownCert);
                     }
                 }
-                if (permission.knownCerts == null) {
+                if (permission.getKnownCerts() == null) {
                     Slog.w(TAG, packageName + " defines a knownSigner permission but"
                             + " the provided knownCerts resource is null");
                 }
@@ -124,12 +120,12 @@
 
             // For now only platform runtime permissions can be restricted
             if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
-                permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
-                permission.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_HARD_RESTRICTED);
+                permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_SOFT_RESTRICTED);
             } else {
                 // The platform does not get to specify conflicting permissions
-                if ((permission.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
-                        && (permission.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+                if ((permission.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                        && (permission.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
                     throw new IllegalStateException("Permission cannot be both soft and hard"
                             + " restricted: " + permission.getName());
                 }
@@ -138,7 +134,8 @@
             sa.recycle();
         }
 
-        permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
+        permission.setProtectionLevel(
+                PermissionInfo.fixProtectionLevel(permission.getProtectionLevel()));
 
         final int otherProtectionFlags = permission.getProtectionFlags()
                 & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
@@ -188,8 +185,8 @@
                     + permission.getName());
         }
 
-        permission.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
-        permission.tree = true;
+        permission.setProtectionLevel(PermissionInfo.PROTECTION_NORMAL)
+                .setTree(true);
 
         return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission,
                 input);
@@ -219,12 +216,12 @@
             }
 
             // @formatter:off
-            permissionGroup.requestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
-            permissionGroup.backgroundRequestResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0);
-            permissionGroup.backgroundRequestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
-            permissionGroup.requestRes = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0);
-            permissionGroup.flags = sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0);
-            permissionGroup.priority = sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0);
+            permissionGroup.setRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
+                    .setBackgroundRequestResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
+                    .setBackgroundRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
+                    .setRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0))
+                    .setPriority(sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0))
+                    .setFlags(sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0));
             // @formatter:on
         } finally {
             sa.recycle();
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index 54a60d3..c39d6b1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -32,22 +32,22 @@
 import java.util.Set;
 
 /** @hide */
-@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
         genBuilder = false)
 public class ParsedProcess implements Parcelable {
 
     @NonNull
-    protected String name;
+    private String name;
     @NonNull
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
-    protected Set<String> deniedPermissions = emptySet();
+    private Set<String> deniedPermissions = emptySet();
 
     @ApplicationInfo.GwpAsanMode
-    protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
     @ApplicationInfo.MemtagMode
-    protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+    private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
     @ApplicationInfo.NativeHeapZeroInitialized
-    protected int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
+    private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
 
     public ParsedProcess() {
     }
@@ -63,7 +63,7 @@
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -128,6 +128,46 @@
     }
 
     @DataClass.Generated.Member
+    public @NonNull ParsedProcess setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcess setDeniedPermissions(@NonNull Set<String> value) {
+        deniedPermissions = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcess setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
+        gwpAsanMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcess setMemtagMode(@ApplicationInfo.MemtagMode int value) {
+        memtagMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcess setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        return this;
+    }
+
+    @DataClass.Generated.Member
     static Parcelling<Set<String>> sParcellingForDeniedPermissions =
             Parcelling.Cache.get(
                     Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -202,10 +242,10 @@
     };
 
     @DataClass.Generated(
-            time = 1615850515058L,
-            codegenVersion = "1.0.22",
+            time = 1623692988845L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
-            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprotected @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprotected @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index e417e74..d4e19af 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -85,31 +85,26 @@
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
         try {
             if (perms != null) {
-                proc.deniedPermissions = new ArraySet<>(perms);
+                proc.setDeniedPermissions(new ArraySet<>(perms));
             }
 
-            proc.name = sa.getNonConfigurationString(
+            String processName = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestProcess_process, 0);
             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
-                    pkg.getPackageName(), pkg.getPackageName(), proc.name, flags, separateProcesses,
+                    pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
                     input);
             if (processNameResult.isError()) {
                 return input.error(processNameResult);
             }
 
-            proc.name = processNameResult.getResult();
-
-            if (proc.name == null || proc.name.length() <= 0) {
-                return input.error("<process> does not specify android:process");
-            }
-
-            proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
-            proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
+            proc.setName(processNameResult.getResult());
+            proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
+            proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
             if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
                 Boolean v = sa.getBoolean(
                         R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
-                proc.nativeHeapZeroInitialized =
-                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED;
+                proc.setNativeHeapZeroInitialized(
+                        v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
             }
         } finally {
             sa.recycle();
@@ -129,18 +124,18 @@
             switch (tagName) {
                 case "deny-permission":
                     ParseResult<Set<String>> denyResult = parseDenyPermission(
-                            proc.deniedPermissions, res, parser, input);
+                            proc.getDeniedPermissions(), res, parser, input);
                     result = denyResult;
                     if (denyResult.isSuccess()) {
-                        proc.deniedPermissions = denyResult.getResult();
+                        proc.setDeniedPermissions(denyResult.getResult());
                     }
                     break;
                 case "allow-permission":
                     ParseResult<Set<String>> allowResult = parseAllowPermission(
-                            proc.deniedPermissions, res, parser, input);
+                            proc.getDeniedPermissions(), res, parser, input);
                     result = allowResult;
                     if (allowResult.isSuccess()) {
-                        proc.deniedPermissions = allowResult.getResult();
+                        proc.setDeniedPermissions(allowResult.getResult());
                     }
                     break;
                 default:
@@ -198,9 +193,9 @@
                     result = processResult;
                     if (processResult.isSuccess()) {
                         ParsedProcess process = processResult.getResult();
-                        if (processes.put(process.name, process) != null) {
+                        if (processes.put(process.getName(), process) != null) {
                             result = input.error(
-                                    "<process> specified existing name '" + process.name + "'");
+                                    "<process> specified existing name '" + process.getName() + "'");
                         }
                     }
                     break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java
index fcf6e87..ebf85f7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProvider.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProvider.java
@@ -36,21 +36,21 @@
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
     private String authority;
-    boolean syncable;
+    private boolean syncable;
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String readPermission;
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String writePermission;
-    boolean grantUriPermissions;
-    boolean forceUriPermissions;
-    boolean multiProcess;
-    int initOrder;
+    private boolean grantUriPermissions;
+    private boolean forceUriPermissions;
+    private boolean multiProcess;
+    private int initOrder;
     @Nullable
-    PatternMatcher[] uriPermissionPatterns;
+    private PatternMatcher[] uriPermissionPatterns;
     @Nullable
-    PathPermission[] pathPermissions;
+    private PathPermission[] pathPermissions;
 
     public ParsedProvider(ParsedProvider other) {
         super(other);
@@ -67,24 +67,58 @@
         this.pathPermissions = other.pathPermissions;
     }
 
-    public void setAuthority(String authority) {
+    public ParsedProvider setAuthority(String authority) {
         this.authority = TextUtils.safeIntern(authority);
+        return this;
     }
 
-    public void setSyncable(boolean syncable) {
+    public ParsedProvider setForceUriPermissions(boolean forceUriPermissions) {
+        this.forceUriPermissions = forceUriPermissions;
+        return this;
+    }
+
+    public ParsedProvider setGrantUriPermissions(boolean grantUriPermissions) {
+        this.grantUriPermissions = grantUriPermissions;
+        return this;
+    }
+
+    public ParsedProvider setInitOrder(int initOrder) {
+        this.initOrder = initOrder;
+        return this;
+    }
+
+    public ParsedProvider setMultiProcess(boolean multiProcess) {
+        this.multiProcess = multiProcess;
+        return this;
+    }
+
+    public ParsedProvider setPathPermissions(PathPermission[] pathPermissions) {
+        this.pathPermissions = pathPermissions;
+        return this;
+    }
+
+    public ParsedProvider setSyncable(boolean syncable) {
         this.syncable = syncable;
+        return this;
     }
 
-    public void setReadPermission(String readPermission) {
+    public ParsedProvider setReadPermission(String readPermission) {
         // Empty string must be converted to null
         this.readPermission = TextUtils.isEmpty(readPermission)
                 ? null : readPermission.intern();
+        return this;
     }
 
-    public void setWritePermission(String writePermission) {
+    public ParsedProvider setUriPermissionPatterns(PatternMatcher[] uriPermissionPatterns) {
+        this.uriPermissionPatterns = uriPermissionPatterns;
+        return this;
+    }
+
+    public ParsedProvider setWritePermission(String writePermission) {
         // Empty string must be converted to null
         this.writePermission = TextUtils.isEmpty(writePermission)
                 ? null : writePermission.intern();
+        return this;
     }
 
     public String toString() {
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 28fd919..0b03567 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -85,10 +85,10 @@
             // For compatibility, applications targeting API level 16 or lower
             // should have their content providers exported by default, unless they
             // specify otherwise.
-            provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
-                    targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1);
-
-            provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false);
+            provider.setSyncable(sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_syncable, false))
+                    .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
+                            targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
 
             String permission = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestProvider_permission, 0);
@@ -113,16 +113,21 @@
                 provider.setWritePermission(writePermission);
             }
 
-            provider.grantUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false);
-            provider.forceUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions, false);
-            provider.multiProcess = sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false);
-            provider.initOrder = sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0);
+            provider.setGrantUriPermissions(
+                    sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false))
+                    .setForceUriPermissions(
+                            sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions,
+                                    false))
+                    .setMultiProcess(
+                            sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false))
+                    .setInitOrder(sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0))
+                    .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
+                            R.styleable.AndroidManifestProvider_singleUser, sa));
 
-            provider.flags |= flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa);
-
-            visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
             if (visibleToEphemeral) {
-                provider.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                provider.setFlags(provider.getFlags() | ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP);
                 pkg.setVisibleToInstantApps(true);
             }
         } finally {
@@ -174,7 +179,7 @@
                     result = intentResult;
                     if (intentResult.isSuccess()) {
                         ParsedIntentInfo intent = intentResult.getResult();
-                        provider.order = Math.max(intent.getOrder(), provider.order);
+                        provider.setOrder(Math.max(intent.getOrder(), provider.getOrder()));
                         provider.addIntent(intent);
                     }
                     break;
@@ -245,17 +250,17 @@
             }
 
             if (pa != null) {
-                if (provider.uriPermissionPatterns == null) {
-                    provider.uriPermissionPatterns = new PatternMatcher[1];
-                    provider.uriPermissionPatterns[0] = pa;
+                if (provider.getUriPermissionPatterns() == null) {
+                    provider.setUriPermissionPatterns(new PatternMatcher[1]);
+                    provider.getUriPermissionPatterns()[0] = pa;
                 } else {
-                    final int N = provider.uriPermissionPatterns.length;
+                    final int N = provider.getUriPermissionPatterns().length;
                     PatternMatcher[] newp = new PatternMatcher[N + 1];
-                    System.arraycopy(provider.uriPermissionPatterns, 0, newp, 0, N);
+                    System.arraycopy(provider.getUriPermissionPatterns(), 0, newp, 0, N);
                     newp[N] = pa;
-                    provider.uriPermissionPatterns = newp;
+                    provider.setUriPermissionPatterns(newp);
                 }
-                provider.grantUriPermissions = true;
+                provider.setGrantUriPermissions(true);
             } else {
                 if (PackageParser.RIGID_PARSER) {
                     return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
@@ -349,15 +354,15 @@
             }
 
             if (pa != null) {
-                if (provider.pathPermissions == null) {
-                    provider.pathPermissions = new PathPermission[1];
-                    provider.pathPermissions[0] = pa;
+                if (provider.getPathPermissions() == null) {
+                    provider.setPathPermissions(new PathPermission[1]);
+                    provider.getPathPermissions()[0] = pa;
                 } else {
-                    final int N = provider.pathPermissions.length;
+                    final int N = provider.getPathPermissions().length;
                     PathPermission[] newp = new PathPermission[N + 1];
-                    System.arraycopy(provider.pathPermissions, 0, newp, 0, N);
+                    System.arraycopy(provider.getPathPermissions(), 0, newp, 0, N);
                     newp[N] = pa;
-                    provider.pathPermissions = newp;
+                    provider.setPathPermissions(newp);
                 }
             } else {
                 if (PackageParser.RIGID_PARSER) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java
index 7adb262..471d346 100644
--- a/core/java/android/content/pm/parsing/component/ParsedService.java
+++ b/core/java/android/content/pm/parsing/component/ParsedService.java
@@ -30,7 +30,7 @@
 /** @hide **/
 public class ParsedService extends ParsedMainComponent {
 
-    int foregroundServiceType;
+    private int foregroundServiceType;
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String permission;
@@ -41,6 +41,11 @@
         this.permission = other.permission;
     }
 
+    public ParsedService setForegroundServiceType(int foregroundServiceType) {
+        this.foregroundServiceType = foregroundServiceType;
+        return this;
+    }
+
     public ParsedMainComponent setPermission(String permission) {
         // Empty string must be converted to null
         this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index ae107ce..59267f9 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -78,33 +78,32 @@
 
             setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
             if (setExported) {
-                service.exported = sa.getBoolean(R.styleable.AndroidManifestService_exported,
-                        false);
+                service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported,
+                        false));
             }
 
             String permission = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestService_permission, 0);
             service.setPermission(permission != null ? permission : pkg.getPermission());
 
-            service.foregroundServiceType = sa.getInt(
+            service.setForegroundServiceType(sa.getInt(
                     R.styleable.AndroidManifestService_foregroundServiceType,
-                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
-
-            service.flags |= flag(ServiceInfo.FLAG_STOP_WITH_TASK,
-                    R.styleable.AndroidManifestService_stopWithTask, sa)
-                    | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
-                    R.styleable.AndroidManifestService_isolatedProcess, sa)
-                    | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
-                    R.styleable.AndroidManifestService_externalService, sa)
-                    | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
-                    R.styleable.AndroidManifestService_useAppZygote, sa)
-                    | flag(ServiceInfo.FLAG_SINGLE_USER,
-                    R.styleable.AndroidManifestService_singleUser, sa);
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE))
+                    .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK,
+                            R.styleable.AndroidManifestService_stopWithTask, sa)
+                            | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
+                            R.styleable.AndroidManifestService_isolatedProcess, sa)
+                            | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
+                            R.styleable.AndroidManifestService_externalService, sa)
+                            | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
+                            R.styleable.AndroidManifestService_useAppZygote, sa)
+                            | flag(ServiceInfo.FLAG_SINGLE_USER,
+                            R.styleable.AndroidManifestService_singleUser, sa)));
 
             visibleToEphemeral = sa.getBoolean(
                     R.styleable.AndroidManifestService_visibleToInstantApps, false);
             if (visibleToEphemeral) {
-                service.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
                 pkg.setVisibleToInstantApps(true);
             }
         } finally {
@@ -139,7 +138,7 @@
                     parseResult = intentResult;
                     if (intentResult.isSuccess()) {
                         ParsedIntentInfo intent = intentResult.getResult();
-                        service.order = Math.max(intent.getOrder(), service.order);
+                        service.setOrder(Math.max(intent.getOrder(), service.getOrder()));
                         service.addIntent(intent);
                     }
                     break;
@@ -172,7 +171,7 @@
                     return input.error(exportedCheckResult);
                 }
             }
-            service.exported = hasIntentFilters;
+            service.setExported(hasIntentFilters);
         }
 
         return input.success(service);
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
index adf8da0..020784d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
@@ -24,6 +24,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -33,50 +36,135 @@
  *
  * @hide
  */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
 public class ParsedUsesPermission implements Parcelable {
-    /** Name of the permission requested */
-    public @NonNull String name;
 
-    /** Set of flags that should apply to this permission request. */
-    public @UsesPermissionFlags int usesPermissionFlags;
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @NonNull
+    private String name;
+
+    @UsesPermissionFlags
+    private int usesPermissionFlags;
 
     /**
-     * Strong assertion by a developer that they will never use this permission
-     * to derive the physical location of the device, regardless of
-     * ACCESS_FINE_LOCATION and/or ACCESS_COARSE_LOCATION being granted.
+     * Strong assertion by a developer that they will never use this permission to derive the
+     * physical location of the device, regardless of ACCESS_FINE_LOCATION and/or
+     * ACCESS_COARSE_LOCATION being granted.
      */
     public static final int FLAG_NEVER_FOR_LOCATION =
             PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
 
-    /** @hide */
+    /**
+     * @hide
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_NEVER_FOR_LOCATION
     })
     public @interface UsesPermissionFlags {}
 
-    public ParsedUsesPermission(@NonNull String name,
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedUsesPermission(
+            @NonNull String name,
             @UsesPermissionFlags int usesPermissionFlags) {
-        this.name = name.intern();
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
         this.usesPermissionFlags = usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @UsesPermissionFlags int getUsesPermissionFlags() {
+        return usesPermissionFlags;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermission setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermission setUsesPermissionFlags(@UsesPermissionFlags int value) {
+        usesPermissionFlags = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                UsesPermissionFlags.class, null, usesPermissionFlags);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForName =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForName == null) {
+            sParcellingForName = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
     }
 
     @Override
+    @DataClass.Generated.Member
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        sForInternedString.parcel(this.name, dest, flags);
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        sParcellingForName.parcel(name, dest, flags);
         dest.writeInt(usesPermissionFlags);
     }
 
     @Override
-    public int describeContents() {
-        return 0;
-    }
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
 
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
     protected ParsedUsesPermission(@NonNull Parcel in) {
-        this.name = sForInternedString.unparcel(in);
-        this.usesPermissionFlags = in.readInt();
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = sParcellingForName.unparcel(in);
+        int _usesPermissionFlags = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.usesPermissionFlags = _usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
     }
 
+    @DataClass.Generated.Member
     public static final @NonNull Parcelable.Creator<ParsedUsesPermission> CREATOR
             = new Parcelable.Creator<ParsedUsesPermission>() {
         @Override
@@ -89,4 +177,17 @@
             return new ParsedUsesPermission(in);
         }
     };
+
+    @DataClass.Generated(
+            time = 1626207990753L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\npublic static final  int FLAG_NEVER_FOR_LOCATION\nclass ParsedUsesPermission extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 7ac78b7..324612d 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -119,7 +119,6 @@
             // how many APKs they're going through.
             mDeferredErrors.erase();
         }
-        mPackageName = null;
         mTargetSdkVersion = null;
         return this;
     }
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
new file mode 100644
index 0000000..9198b95
--- /dev/null
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.permission;
+
+import android.Manifest;
+import android.content.pm.parsing.component.ParsedUsesPermission;
+
+/**
+ * Implements compatibility support for permissions, and old applications
+ * will be automatically granted it.
+ *
+ * Compatibility permissions are permissions that are automatically granted to
+ * packages that target an SDK prior to when the permission was introduced.
+ * Sometimes the platform makes breaking behaviour changes and hides the legacy
+ * behaviour behind a permission. In these instances, we ensure applications
+ * targeting older platform versions are implicitly granted the correct set of
+ * permissions.
+ *
+ * @hide
+ */
+public class CompatibilityPermissionInfo extends ParsedUsesPermission {
+
+    public final int sdkVersion;
+
+    /**
+     * List of new permissions that have been added since 1.0.
+     *
+     * @hide
+     */
+    public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
+            new CompatibilityPermissionInfo[]{
+                    new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                            android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
+                    new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
+                            android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
+            };
+
+    private CompatibilityPermissionInfo(String name, int sdkVersion,
+            @UsesPermissionFlags int usesPermissionFlags) {
+        super(name, usesPermissionFlags);
+        this.sdkVersion = sdkVersion;
+    }
+}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index c1a8396..47cf28b 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,6 @@
  */
 package android.content.pm.split;
 
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
@@ -52,23 +48,21 @@
     }
 
     private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
-            throws PackageParserException {
+            throws IllegalArgumentException {
         if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
                 && !ApkLiteParseUtils.isApkPath(path)) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                    "Invalid package file: " + path);
+            throw new IllegalArgumentException("Invalid package file: " + path);
         }
 
         try {
             return ApkAssets.loadFromPath(path);
         } catch (IOException e) {
-            throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
-                    "Failed to load APK at path " + path, e);
+            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
         }
     }
 
     @Override
-    public AssetManager getBaseAssetManager() throws PackageParserException {
+    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
         if (mCachedAssetManager != null) {
             return mCachedAssetManager;
         }
@@ -97,7 +91,7 @@
     }
 
     @Override
-    public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+    public AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException {
         return getBaseAssetManager();
     }
 
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index e5c2158..2b9a17a 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,11 +15,7 @@
  */
 package android.content.pm.split;
 
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-
 import android.annotation.NonNull;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
@@ -40,7 +36,7 @@
  * is to be used when an application opts-in to isolated split loading.
  * @hide
  */
-public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArgumentException>
         implements SplitAssetLoader {
     private final String[] mSplitPaths;
     private final @ParseFlags int mFlags;
@@ -67,18 +63,16 @@
     }
 
     private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
-            throws PackageParserException {
+            throws IllegalArgumentException {
         if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
                 && !ApkLiteParseUtils.isApkPath(path)) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                    "Invalid package file: " + path);
+            throw new IllegalArgumentException("Invalid package file: " + path);
         }
 
         try {
             return ApkAssets.loadFromPath(path);
         } catch (IOException e) {
-            throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
-                    "Failed to load APK at path " + path, e);
+            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
         }
     }
 
@@ -92,7 +86,7 @@
 
     @Override
     protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
-            int parentSplitIdx) throws PackageParserException {
+            int parentSplitIdx) throws IllegalArgumentException {
         final ArrayList<ApkAssets> assets = new ArrayList<>();
 
         // Include parent ApkAssets.
@@ -114,13 +108,13 @@
     }
 
     @Override
-    public AssetManager getBaseAssetManager() throws PackageParserException {
+    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
         loadDependenciesForSplit(0);
         return mCachedAssetManagers[0];
     }
 
     @Override
-    public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
+    public AssetManager getSplitAssetManager(int idx) throws IllegalArgumentException {
         // Since we insert the base at position 0, and PackageParser keeps splits separate from
         // the base, we need to adjust the index.
         loadDependenciesForSplit(idx + 1);
diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java
index 7584e15f..d314e06 100644
--- a/core/java/android/content/pm/split/SplitAssetLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetLoader.java
@@ -15,19 +15,18 @@
  */
 package android.content.pm.split;
 
-import android.content.pm.PackageParser;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 
 /**
- * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+ * Simple interface for loading base Assets and Splits. Used by ParsingPackageUtils when parsing
  * split APKs.
  *
  * @hide
  */
 public interface SplitAssetLoader extends AutoCloseable {
-    AssetManager getBaseAssetManager() throws PackageParser.PackageParserException;
-    AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException;
+    AssetManager getBaseAssetManager() throws IllegalArgumentException;
+    AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException;
 
     ApkAssets getBaseApkAssets();
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 572a8a8..ad4e64b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -506,6 +506,7 @@
         if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION
                 || type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE
                 || type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE
+                || type == Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT
                 || type == Sensor.TYPE_WRIST_TILT_GESTURE
                 || type == Sensor.TYPE_DYNAMIC_SENSOR_META || type == Sensor.TYPE_HINGE_ANGLE) {
             wakeUpSensor = true;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ecfc0d5..a3be415 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -346,7 +346,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface HdmiCecControl {}
 
-    // -- Supported HDM-CEC versions.
+    // -- Supported HDMI-CEC versions.
     /**
      * Version constant for HDMI-CEC v1.4b.
      *
@@ -371,23 +371,67 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface HdmiCecVersion {}
 
+    // -- Whether the Routing Control feature is enabled or disabled.
+    /**
+     * Routing Control feature enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ROUTING_CONTROL_ENABLED = 1;
+    /**
+     * Routing Control feature disabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ROUTING_CONTROL_DISABLED = 0;
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "ROUTING_CONTROL_" }, value = {
+            ROUTING_CONTROL_ENABLED,
+            ROUTING_CONTROL_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RoutingControl {}
+
     // -- Scope of CEC power control messages sent by a playback device.
     /**
-     * Send CEC power control messages to TV only.
+     * Send CEC power control messages to TV only:
+     * Upon going to sleep, send {@code <Standby>} to TV only.
+     * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} but do not turn on the
+     * Audio system via {@code <System Audio Mode Request>}.
      *
      * @hide
      */
     @SystemApi
     public static final String POWER_CONTROL_MODE_TV = "to_tv";
     /**
-     * Broadcast CEC power control messages to all devices in the network.
+     * Send CEC power control messages to TV and Audio System:
+     * Upon going to sleep, send {@code <Standby>} to TV and Audio system.
+     * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+     * the Audio system via {@code <System Audio Mode Request>}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
+    /**
+     * Broadcast CEC power control messages to all devices in the network:
+     * Upon going to sleep, send {@code <Standby>} to all devices in the network.
+     * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+     * the Audio system via {@code <System Audio Mode Request>}.
      *
      * @hide
      */
     @SystemApi
     public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
     /**
-     * Don't send any CEC power control messages.
+     * Don't send any CEC power control messages:
+     * Upon going to sleep, do not send any {@code <Standby>} message.
+     * Upon waking up, do not turn on the TV via {@code <One Touch Play>} and do not turn on the
+     * Audio system via {@code <System Audio Mode Request>}.
      *
      * @hide
      */
@@ -398,6 +442,7 @@
      */
     @StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = {
             POWER_CONTROL_MODE_TV,
+            POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
             POWER_CONTROL_MODE_BROADCAST,
             POWER_CONTROL_MODE_NONE
     })
@@ -429,6 +474,31 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActiveSourceLostBehavior {}
 
+    // -- Whether System Audio Control is enabled or disabled.
+    /**
+     * System Audio Control enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1;
+    /**
+     * System Audio Control disabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0;
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "SYSTEM_AUDIO_CONTROL_" }, value = {
+            SYSTEM_AUDIO_CONTROL_ENABLED,
+            SYSTEM_AUDIO_CONTROL_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemAudioControl {}
+
     // -- Whether System Audio Mode muting is enabled or disabled.
     /**
      * System Audio Mode muting enabled.
@@ -710,6 +780,13 @@
     @SystemApi
     public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
     /**
+     * Name of a setting deciding whether the Routing Control feature is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+    /**
      * Name of a setting deciding on the power control mode.
      *
      * @hide
@@ -725,6 +802,14 @@
     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 Control is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL =
+            "system_audio_control";
+    /**
      * Name of a setting deciding whether System Audio Muting is allowed.
      *
      * @hide
@@ -778,7 +863,7 @@
     public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
             "tv_wake_on_one_touch_play";
     /**
-     * Name of a setting deciding whether the device will also turn off other CEC devices
+     * Name of a setting deciding whether the TV will also turn off other CEC devices
      * when it goes to standby mode.
      *
      * @hide
@@ -842,6 +927,7 @@
         CEC_SETTING_NAME_HDMI_CEC_VERSION,
         CEC_SETTING_NAME_POWER_CONTROL_MODE,
         CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+        CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
         CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
         CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
         CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -2021,6 +2107,56 @@
     }
 
     /**
+     * Set the status of Routing Control feature.
+     *
+     * <p>This allows to enable/disable Routing Control on the device.
+     * If enabled, the switch device will route to the correct input source on
+     * receiving Routing Control related messages. If disabled, you can only
+     * switch the input via controls on this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setRoutingControl(@NonNull @RoutingControl int value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the current status of Routing Control feature.
+     *
+     * <p>Reflects whether Routing Control is currently enabled on the device.
+     * If enabled, the switch device will route to the correct input source on
+     * receiving Routing Control related messages. If disabled, you can only
+     * switch the input via controls on this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RoutingControl
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public int getRoutingControl() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Set the status of Power Control.
      *
      * <p>Specifies to which devices Power Control messages should be sent:
@@ -2114,6 +2250,58 @@
     }
 
     /**
+     * Set the current status of System Audio Control.
+     *
+     * <p>Sets whether HDMI System Audio Control feature is enabled. If enabled,
+     * TV or Audio System will try to turn on the System Audio Mode if there's a
+     * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+     * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+     * System Audio Mode will never be activated.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the current status of System Audio Control.
+     *
+     * <p>Reflects whether HDMI System Audio Control feature is enabled. If enabled,
+     * TV or Audio System will try to turn on the System Audio Mode if there's a
+     * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+     * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+     * System Audio Mode will never be activated.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @SystemAudioControl
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public int getSystemAudioControl() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Set the current status of System Audio Mode muting.
      *
      * <p>Sets whether the device should be muted when System Audio Mode is turned off.
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 60d43fd..861e440 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -3,5 +3,4 @@
 include /services/core/java/com/android/server/display/OWNERS
 
 marvinramin@google.com
-nchalko@google.com
 lcnathalie@google.com
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 17116e2..e5d8620 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -435,7 +435,7 @@
     /**
      * Enables an InputDevice.
      * <p>
-     * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+     * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
      * </p>
      *
      * @param id The input device Id.
@@ -454,7 +454,7 @@
     /**
      * Disables an InputDevice.
      * <p>
-     * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+     * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
      * </p>
      *
      * @param id The input device Id.
@@ -831,7 +831,7 @@
      * Sets the TouchCalibration to apply to the specified input device's coordinates.
      * <p>
      * This method may have the side-effect of causing the input device in question
-     * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+     * to be reconfigured. Requires {@link android.Manifest.permission.SET_INPUT_CALIBRATION}.
      * </p>
      *
      * @param inputDeviceDescriptor The input device descriptor.
@@ -874,7 +874,7 @@
     /**
      * Sets the mouse pointer speed.
      * <p>
-     * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
+     * Requires {@link android.Manifest.permission.WRITE_SETTINGS}.
      * </p>
      *
      * @param context The application context.
@@ -1285,7 +1285,7 @@
      * @param inputPort The port of the input device.
      * @param displayPort The physical port of the associated display.
      * <p>
-     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+     * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
      * </p>
      * @hide
      */
@@ -1302,7 +1302,7 @@
      * static association for the cleared input port will be restored.
      * @param inputPort The port of the input device to be cleared.
      * <p>
-     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+     * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
      * </p>
      * @hide
      */
@@ -1320,7 +1320,7 @@
      * @param inputDeviceName The name of the input device.
      * @param displayUniqueId The unique id of the associated display.
      * <p>
-     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+     * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
      * </p>
      * @hide
      */
@@ -1337,7 +1337,7 @@
      * Removes a runtime association between the input device and display.
      * @param inputDeviceName The name of the input device.
      * <p>
-     * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+     * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
      * </p>
      * @hide
      */
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 21ac71c..bcdd519 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -152,10 +152,7 @@
      * @see NanoAppMessage
      * @see ContextHubTransaction.Result
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @ContextHubTransaction.Result
     public int sendMessageToNanoApp(@NonNull NanoAppMessage message) {
         Objects.requireNonNull(message, "NanoAppMessage cannot be null");
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 9af0e09..96923b0 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -47,9 +47,7 @@
  * A class that exposes the Context hubs on a device to applications.
  *
  * Please note that this class is not expected to be used by unbundled applications. Also, calling
- * applications are expected to have LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permissions to use this
- * class. Use of LOCATION_HARDWARE to enable access to these APIs is deprecated and may be removed
- * in the future - all applications are recommended to move to the ACCESS_CONTEXT_HUB permission.
+ * applications are expected to have the ACCESS_CONTEXT_HUB permission to use this class.
  *
  * @hide
  */
@@ -253,10 +251,7 @@
      *             new APIs.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int[] getContextHubHandles() {
         try {
             return mService.getContextHubHandles();
@@ -277,10 +272,7 @@
      *             new APIs.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public ContextHubInfo getContextHubInfo(int hubHandle) {
         try {
             return mService.getContextHubInfo(hubHandle);
@@ -311,10 +303,7 @@
      * @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
         try {
             return mService.loadNanoApp(hubHandle, app);
@@ -341,10 +330,7 @@
      * @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int unloadNanoApp(int nanoAppHandle) {
         try {
             return mService.unloadNanoApp(nanoAppHandle);
@@ -384,10 +370,7 @@
      *             for loaded nanoapps.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
         try {
             return mService.getNanoAppInstanceInfo(nanoAppHandle);
@@ -410,10 +393,7 @@
      *             for loaded nanoapps.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
         try {
             return mService.findNanoAppOnHub(hubHandle, filter);
@@ -448,10 +428,7 @@
      *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
         try {
             return mService.sendMessage(hubHandle, nanoAppHandle, message);
@@ -467,10 +444,7 @@
      *
      * @see ContextHubInfo
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public List<ContextHubInfo> getContextHubs() {
         try {
             return mService.getContextHubs();
@@ -547,10 +521,7 @@
      *
      * @see NanoAppBinary
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubTransaction<Void> loadNanoApp(
             @NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -579,10 +550,7 @@
      *
      * @throws NullPointerException if hubInfo is null
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubTransaction<Void> unloadNanoApp(
             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -610,10 +578,7 @@
      *
      * @throws NullPointerException if hubInfo is null
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubTransaction<Void> enableNanoApp(
             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -641,10 +606,7 @@
      *
      * @throws NullPointerException if hubInfo is null
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubTransaction<Void> disableNanoApp(
             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -671,10 +633,7 @@
      *
      * @throws NullPointerException if hubInfo is null
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
             @NonNull ContextHubInfo hubInfo) {
         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -829,10 +788,7 @@
      *
      * @see ContextHubClientCallback
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubClient createClient(
             @Nullable Context context, @NonNull ContextHubInfo hubInfo,
             @NonNull @CallbackExecutor Executor executor,
@@ -876,10 +832,7 @@
      * {@link #createClient(ContextHubInfo, Executor, String, ContextHubClientCallback)}
      * with the {@link Context} being set to null.
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubClient createClient(
             @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
             @NonNull @CallbackExecutor Executor executor) {
@@ -890,10 +843,7 @@
      * Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
      * with the executor using the main thread's Looper.
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubClient createClient(
             @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
         return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
@@ -931,6 +881,9 @@
      * on the provided PendingIntent, then the client will be automatically unregistered by the
      * service.
      *
+     * Note that the {@link PendingIntent} supplied to this API must be mutable for Intent
+     * notifications to work.
+     *
      * @param context       the context of the application. If a PendingIntent client is recreated,
      * the latest state in the context will be used and old state will be discarded
      * @param hubInfo       the hub to attach this client to
@@ -938,19 +891,20 @@
      * @param nanoAppId     the ID of the nanoapp that Intent events will be generated for
      * @return the registered client object
      *
-     * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or an immutable
+     *                                  PendingIntent was supplied
      * @throws IllegalStateException    if there were too many registered clients at the service
      * @throws NullPointerException     if pendingIntent or hubInfo is null
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubClient createClient(
             @Nullable Context context, @NonNull ContextHubInfo hubInfo,
             @NonNull PendingIntent pendingIntent, long nanoAppId) {
         Objects.requireNonNull(pendingIntent);
         Objects.requireNonNull(hubInfo);
+        if (pendingIntent.isImmutable()) {
+            throw new IllegalArgumentException("PendingIntent must be mutable");
+        }
 
         ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
 
@@ -975,10 +929,7 @@
      * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
      * with {@link Context} being set to null.
      */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.LOCATION_HARDWARE,
-            android.Manifest.permission.ACCESS_CONTEXT_HUB
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public ContextHubClient createClient(
             @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
         return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a5c9a7f..ad0dc09 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -19,19 +19,19 @@
 import android.annotation.Nullable;
 import android.media.AudioFormat;
 import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
 import android.os.ParcelFileDescriptor;
 import android.os.SharedMemory;
 
@@ -43,7 +43,7 @@
 class ConversionUtil {
     public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
             SoundTriggerModuleDescriptor aidlDesc) {
-        SoundTriggerModuleProperties properties = aidlDesc.properties;
+        Properties properties = aidlDesc.properties;
         return new SoundTrigger.ModuleProperties(
                 aidlDesc.handle,
                 properties.implementor,
@@ -194,19 +194,19 @@
     }
 
     public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
-            int modelHandle, RecognitionEvent aidlEvent) {
+            int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
         // The API recognition event doesn't allow for a null audio format, even though it doesn't
         // always make sense. We thus replace it with a default.
         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
         return new SoundTrigger.GenericRecognitionEvent(
                 aidlEvent.status,
-                modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
+                modelHandle, aidlEvent.captureAvailable, captureSession,
                 aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
                 audioFormat, aidlEvent.data);
     }
 
     public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
-            int modelHandle,
+            int modelHandle, int captureSession,
             PhraseRecognitionEvent aidlEvent) {
         SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
                 new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
@@ -218,7 +218,7 @@
         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
         return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
                 aidlEvent.common.captureAvailable,
-                aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
+                captureSession, aidlEvent.common.captureDelayMs,
                 aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
                 audioFormat, aidlEvent.common.data,
                 apiExtras);
@@ -328,9 +328,9 @@
     public static int api2aidlModelParameter(int apiParam) {
         switch (apiParam) {
             case ModelParams.THRESHOLD_FACTOR:
-                return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
+                return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR;
             default:
-                return android.media.soundtrigger_middleware.ModelParameter.INVALID;
+                return android.media.soundtrigger.ModelParameter.INVALID;
         }
     }
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 11f3e45..163e6f0 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
 import static android.Manifest.permission.RECORD_AUDIO;
 import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
+import static android.system.OsConstants.EBUSY;
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.ENODEV;
 import static android.system.OsConstants.ENOSYS;
@@ -41,9 +42,9 @@
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.Identity;
 import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.Status;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -91,6 +92,8 @@
     public static final int STATUS_DEAD_OBJECT = -EPIPE;
     /** @hide */
     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
+    /** @hide */
+    public static final int STATUS_BUSY = -EBUSY;
 
     /*****************************************************************************
      * A ModuleProperties describes a given sound trigger hardware module
@@ -1835,120 +1838,6 @@
         }
     }
 
-    /**
-     *  Status codes for {@link SoundModelEvent}
-     */
-    /**
-     * Sound Model was updated
-     *
-     * @hide
-     */
-    public static final int SOUNDMODEL_STATUS_UPDATED = 0;
-
-    /**
-     *  A SoundModelEvent is provided by the
-     *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
-     *  callback when a sound model has been updated by the implementation
-     *
-     *  @hide
-     */
-    public static class SoundModelEvent implements Parcelable {
-        /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
-        public final int status;
-        /** The updated sound model handle */
-        public final int soundModelHandle;
-        /** New sound model data */
-        @NonNull
-        public final byte[] data;
-
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
-            this.status = status;
-            this.soundModelHandle = soundModelHandle;
-            this.data = data != null ? data : new byte[0];
-        }
-
-        public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
-                = new Parcelable.Creator<SoundModelEvent>() {
-            public SoundModelEvent createFromParcel(Parcel in) {
-                return SoundModelEvent.fromParcel(in);
-            }
-
-            public SoundModelEvent[] newArray(int size) {
-                return new SoundModelEvent[size];
-            }
-        };
-
-        private static SoundModelEvent fromParcel(Parcel in) {
-            int status = in.readInt();
-            int soundModelHandle = in.readInt();
-            byte[] data = in.readBlob();
-            return new SoundModelEvent(status, soundModelHandle, data);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(status);
-            dest.writeInt(soundModelHandle);
-            dest.writeBlob(data);
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + Arrays.hashCode(data);
-            result = prime * result + soundModelHandle;
-            result = prime * result + status;
-            return result;
-        }
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            SoundModelEvent other = (SoundModelEvent) obj;
-            if (!Arrays.equals(data, other.data))
-                return false;
-            if (soundModelHandle != other.soundModelHandle)
-                return false;
-            if (status != other.status)
-                return false;
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
-                    + ", data=" + (data == null ? 0 : data.length) + "]";
-        }
-    }
-
-    /**
-     *  Native service state. {@link StatusListener#onServiceStateChange(int)}
-     */
-    // Keep in sync with system/core/include/system/sound_trigger.h
-    /**
-     * Sound trigger service is enabled
-     *
-     * @hide
-     */
-    public static final int SERVICE_STATE_ENABLED = 0;
-    /**
-     * Sound trigger service is disabled
-     *
-     * @hide
-     */
-    public static final int SERVICE_STATE_DISABLED = 1;
     private static Object mServiceLock = new Object();
     private static ISoundTriggerMiddlewareService mService;
 
@@ -1975,6 +1864,8 @@
                     return STATUS_DEAD_OBJECT;
                 case Status.INTERNAL_ERROR:
                     return STATUS_ERROR;
+                case Status.RESOURCE_CONTENTION:
+                    return STATUS_BUSY;
             }
             return STATUS_ERROR;
         }
@@ -2224,27 +2115,28 @@
      *
      * @hide
      */
-    public static interface StatusListener {
+    public interface StatusListener {
         /**
          * Called when recognition succeeds of fails
          */
-        public abstract void onRecognition(RecognitionEvent event);
+        void onRecognition(RecognitionEvent event);
 
         /**
-         * Called when a sound model has been updated
+         * Called when a sound model has been preemptively unloaded by the underlying
+         * implementation.
          */
-        public abstract void onSoundModelUpdate(SoundModelEvent event);
+        void onModelUnloaded(int modelHandle);
 
         /**
-         * Called when the sound trigger native service state changes.
-         * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
-         * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+         * Called whenever underlying conditions change, such that load/start operations that have
+         * previously failed or got preempted may now succeed. This is not a guarantee, merely a
+         * hint that the client may want to retry operations.
          */
-        public abstract void onServiceStateChange(int state);
+        void onResourcesAvailable();
 
         /**
          * Called when the sound trigger native service dies
          */
-        public abstract void onServiceDied();
+        void onServiceDied();
     }
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 431c99d..bf4b514 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -22,13 +22,13 @@
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.Identity;
 import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -48,7 +48,8 @@
 
     private static final int EVENT_RECOGNITION = 1;
     private static final int EVENT_SERVICE_DIED = 2;
-    private static final int EVENT_SERVICE_STATE_CHANGE = 3;
+    private static final int EVENT_RESOURCES_AVAILABLE = 3;
+    private static final int EVENT_MODEL_UNLOADED = 4;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int mId;
     private EventHandlerDelegate mEventHandlerDelegate;
@@ -120,6 +121,7 @@
      * @param soundModelHandle an array of int where the sound model handle will be returned.
      * @return - {@link SoundTrigger#STATUS_OK} in case of success
      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+     *         - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
      *         system permission
      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -181,6 +183,7 @@
      *  recognition mode, keyphrases, users, minimum confidence levels...
      * @return - {@link SoundTrigger#STATUS_OK} in case of success
      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+     *         - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
      *         system permission
      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -333,8 +336,11 @@
                             listener.onRecognition(
                                     (SoundTrigger.RecognitionEvent) msg.obj);
                             break;
-                        case EVENT_SERVICE_STATE_CHANGE:
-                            listener.onServiceStateChange((int) msg.obj);
+                        case EVENT_RESOURCES_AVAILABLE:
+                            listener.onResourcesAvailable();
+                            break;
+                        case EVENT_MODEL_UNLOADED:
+                            listener.onModelUnloaded((Integer) msg.obj);
                             break;
                         case EVENT_SERVICE_DIED:
                             listener.onServiceDied();
@@ -348,27 +354,32 @@
         }
 
         @Override
-        public synchronized void onRecognition(int handle, RecognitionEvent event)
+        public synchronized void onRecognition(int handle, RecognitionEvent event,
+                int captureSession)
                 throws RemoteException {
             Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
-                    ConversionUtil.aidl2apiRecognitionEvent(handle, event));
+                    ConversionUtil.aidl2apiRecognitionEvent(handle, captureSession, event));
             mHandler.sendMessage(m);
         }
 
         @Override
-        public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
+        public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event,
+                int captureSession)
                 throws RemoteException {
             Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
-                    ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
+                    ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, captureSession, event));
             mHandler.sendMessage(m);
         }
 
         @Override
-        public synchronized void onRecognitionAvailabilityChange(boolean available)
-                throws RemoteException {
-            Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
-                    available ? SoundTrigger.SERVICE_STATE_ENABLED
-                            : SoundTrigger.SERVICE_STATE_DISABLED);
+        public void onModelUnloaded(int modelHandle) throws RemoteException {
+            Message m = mHandler.obtainMessage(EVENT_MODEL_UNLOADED, modelHandle);
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public synchronized void onResourcesAvailable() throws RemoteException {
+            Message m = mHandler.obtainMessage(EVENT_RESOURCES_AVAILABLE);
             mHandler.sendMessage(m);
         }
 
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 964d7c1..c29a948 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -384,8 +384,9 @@
             "android.hardware.usb.extra.ACCESSORY_START";
 
     /**
-     * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
-     * This is obtained with SystemClock.elapsedRealtime()
+
+     * A long extra indicating the timestamp just before
+     * sending {@link #ACTION_USB_ACCESSORY_HANDSHAKE}.
      * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
      *
      * {@hide}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3cd13a2..9db41c4 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -18,16 +18,20 @@
 
 import android.annotation.MainThread;
 import android.annotation.NonNull;
-import android.app.Service;
+import android.annotation.Nullable;
+import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
 import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
+import android.os.RemoteException;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSession;
+import android.window.WindowProviderService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -44,9 +48,22 @@
  * implement.  This base class takes care of reporting your InputMethod from
  * the service when clients bind to it, but provides no standard implementation
  * of the InputMethod interface itself.  Derived classes must implement that
- * interface.
+ * interface.</p>
+ *
+ * <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
+ * input may not be the entire screen. For example, some devices may support to show the soft input
+ * on only half of screen.</p>
+ *
+ * <p>In that case, moving the soft input from one half screen to another will trigger a
+ * {@link android.content.res.Resources} update to match the new {@link Configuration} and
+ * this {@link AbstractInputMethodService} may also receive a
+ * {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
+ * </p>
+ *
+ * @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
+ * @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
  */
-public abstract class AbstractInputMethodService extends Service
+public abstract class AbstractInputMethodService extends WindowProviderService
         implements KeyEvent.Callback {
     private InputMethod mInputMethod;
     
@@ -196,16 +213,6 @@
     public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
 
     /**
-     * Dumps the internal state of IME to a protocol buffer output stream.
-     *
-     * @param proto ProtoOutputStream to dump data to.
-     * @param icProto {@link InputConnection} call data in proto format.
-     * @hide
-     */
-    @SuppressWarnings("HiddenAbstractMethod")
-    public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto);
-
-    /**
      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
      * calls on your input method.
      */
@@ -218,9 +225,36 @@
         if (mInputMethod == null) {
             mInputMethod = onCreateInputMethodInterface();
         }
-        return new IInputMethodWrapper(this, mInputMethod);
+        return new IInputMethodWrapper(createInputMethodServiceInternal(), mInputMethod);
     }
-    
+
+    /**
+     * Used to inject custom {@link InputMethodServiceInternal}.
+     *
+     * @return the {@link InputMethodServiceInternal} to be used.
+     */
+    @NonNull
+    InputMethodServiceInternal createInputMethodServiceInternal() {
+        return new InputMethodServiceInternal() {
+            /**
+             * {@inheritDoc}
+             */
+            @NonNull
+            @Override
+            public Context getContext() {
+                return AbstractInputMethodService.this;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+                AbstractInputMethodService.this.dump(fd, fout, args);
+            }
+        };
+    }
+
     /**
      * Implement this to handle trackball events on your input method.
      *
@@ -243,38 +277,33 @@
         return false;
     }
 
-    /**
-     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
-     * permission to the content.
-     *
-     * <p>Default implementation does nothing.</p>
-     *
-     * @param inputContentInfo Content to be temporarily exposed from the input method to the
-     * application.
-     * This cannot be {@code null}.
-     * @param inputConnection {@link InputConnection} with which
-     * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
-     * called.
-     * @return {@code false} if we cannot allow a temporary access permission.
-     * @hide
-     */
-    public void exposeContent(@NonNull InputContentInfo inputContentInfo,
-            @NonNull InputConnection inputConnection) {
-        return;
-    }
-
-    /**
-     * Called when the user took some actions that should be taken into consideration to update the
-     * MRU list for input method rotation.
-     *
-     * @hide
-     */
-    public void notifyUserActionIfNecessary() {
-    }
-
+    // TODO(b/149463653): remove it in T. We missed the API deadline in S.
     /** @hide */
     @Override
     public final boolean isUiContext() {
         return true;
     }
+
+    /** @hide */
+    @Override
+    public final int getWindowType() {
+        return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+    }
+
+    /** @hide */
+    @Override
+    @Nullable
+    public final Bundle getWindowContextOptions() {
+        return null;
+    }
+
+    /** @hide */
+    @Override
+    public final int getInitialDisplayId() {
+        try {
+            return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9198eb7..90990b4 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -47,7 +47,6 @@
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.IInputSessionCallback;
 import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputConnectionWrapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -77,7 +76,7 @@
     private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
     private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
 
-    final WeakReference<AbstractInputMethodService> mTarget;
+    final WeakReference<InputMethodServiceInternal> mTarget;
     final Context mContext;
     @UnsupportedAppUsage
     final HandlerCaller mCaller;
@@ -86,7 +85,7 @@
 
     /**
      * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()}
-     * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been
+     * so that {@link RemoteInputConnection} can query if {@link #unbindInput()} has already been
      * called or not, mainly to avoid unnecessary blocking operations.
      *
      * <p>This field must be set and cleared only from the binder thread(s), where the system
@@ -130,12 +129,12 @@
         }
     }
 
-    public IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod) {
-        mTarget = new WeakReference<>(context);
-        mContext = context.getApplicationContext();
+    IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) {
+        mTarget = new WeakReference<>(imsInternal);
+        mContext = imsInternal.getContext().getApplicationContext();
         mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
         mInputMethod = new WeakReference<>(inputMethod);
-        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+        mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion;
     }
 
     @MainThread
@@ -150,7 +149,7 @@
 
         switch (msg.what) {
             case DO_DUMP: {
-                AbstractInputMethodService target = mTarget.get();
+                InputMethodServiceInternal target = mTarget.get();
                 if (target == null) {
                     return;
                 }
@@ -170,8 +169,8 @@
             case DO_INITIALIZE_INTERNAL: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
-                    inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
-                            (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
+                    inputMethod.initializeInternal((IBinder) args.arg1,
+                            (IInputMethodPrivilegedOperations) args.arg2, msg.arg1);
                 } finally {
                     args.recycle();
                 }
@@ -192,7 +191,7 @@
                 final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
                 SomeArgs moreArgs = (SomeArgs) args.arg5;
                 final InputConnection ic = inputContext != null
-                        ? new InputConnectionWrapper(
+                        ? new RemoteInputConnection(
                                 mTarget, inputContext, moreArgs.argi3, cancellationGroup)
                         : null;
                 info.makeCompatible(mTargetSdkVersion);
@@ -252,11 +251,11 @@
     @BinderThread
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
-        AbstractInputMethodService target = mTarget.get();
+        InputMethodServiceInternal target = mTarget.get();
         if (target == null) {
             return;
         }
-        if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+        if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             
             fout.println("Permission Denial: can't dump InputMethodManager from from pid="
@@ -279,11 +278,10 @@
 
     @BinderThread
     @Override
-    public void initializeInternal(IBinder token, int displayId,
-            IInputMethodPrivilegedOperations privOps, int configChanges) {
+    public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
+            int configChanges) {
         mCaller.executeOrSendMessage(
-                mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
-                        configChanges));
+                mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps));
     }
 
     @BinderThread
@@ -303,7 +301,7 @@
         mCancellationGroup = new CancellationGroup();
         // This IInputContext is guaranteed to implement all the methods.
         final int missingMethodFlags = 0;
-        InputConnection ic = new InputConnectionWrapper(mTarget,
+        InputConnection ic = new RemoteInputConnection(mTarget,
                 IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
                 mCancellationGroup);
         InputBinding nu = new InputBinding(ic, binding);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 881e0cf..dadea67 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -55,7 +55,6 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.annotation.AnyThread;
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
@@ -94,7 +93,6 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
 import android.view.KeyCharacterMap;
@@ -133,6 +131,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -589,7 +588,7 @@
          */
         @MainThread
         @Override
-        public final void initializeInternal(@NonNull IBinder token, int displayId,
+        public final void initializeInternal(@NonNull IBinder token,
                 IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
             if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
                 Log.w(TAG, "The token has already registered, ignore this initialization.");
@@ -599,7 +598,6 @@
             mConfigTracker.onInitialize(configChanges);
             mPrivOps.set(privilegedOperations);
             InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
-            updateInputMethodDisplay(displayId);
             attachToken(token);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -629,29 +627,13 @@
                 throw new IllegalStateException(
                         "attachToken() must be called at most once. token=" + token);
             }
+            attachToWindowToken(token);
             mToken = token;
             mWindow.setToken(token);
         }
 
         /**
          * {@inheritDoc}
-         * @hide
-         */
-        @MainThread
-        @Override
-        public void updateInputMethodDisplay(int displayId) {
-            if (getDisplayId() == displayId) {
-                return;
-            }
-            // Update display for adding IME window to the right display.
-            // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
-            // for update resources & configuration correctly when show soft input
-            // in non-default display.
-            updateDisplay(displayId);
-        }
-
-        /**
-         * {@inheritDoc}
          *
          * <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
          */
@@ -757,7 +739,7 @@
                 return;
             }
             ImeTracing.getInstance().triggerServiceDump(
-                    "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this,
+                    "InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
                     null /* icProto */);
             final boolean wasVisible = isInputViewShown();
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
@@ -814,7 +796,7 @@
             }
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
             ImeTracing.getInstance().triggerServiceDump(
-                    "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this,
+                    "InputMethodService.InputMethodImpl#showSoftInput", mDumper,
                     null /* icProto */);
             final boolean wasVisible = isInputViewShown();
             if (dispatchOnShowInputRequested(flags, false)) {
@@ -2239,7 +2221,7 @@
             return;
         }
 
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
                 null /* icProto */);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
         mDecorViewWasVisible = mDecorViewVisible;
@@ -2318,7 +2300,7 @@
      */
     private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
         ImeTracing.getInstance().triggerServiceDump(
-                "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this,
+                "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
                 null /* icProto */);
         if (setVisible) {
             cancelImeSurfaceRemoval();
@@ -2347,7 +2329,7 @@
 
     public void hideWindow() {
         if (DEBUG) Log.v(TAG, "CALL: hideWindow");
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
                 null /* icProto */);
         mWindowVisible = false;
         finishViews(false /* finishingInput */);
@@ -2420,7 +2402,7 @@
     
     void doFinishInput() {
         if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", mDumper,
                 null /* icProto */);
         finishViews(true /* finishingInput */);
         if (mInputStarted) {
@@ -2437,7 +2419,7 @@
         if (!restarting && mInputStarted) {
             doFinishInput();
         }
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", mDumper,
                 null /* icProto */);
         mInputStarted = true;
         mStartedInputConnection = ic;
@@ -2597,7 +2579,7 @@
      * @param flags Provides additional operating flags.
      */
     public void requestHideSelf(int flags) {
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
                 null /* icProto */);
         mPrivOps.hideMySoftInput(flags);
     }
@@ -2611,7 +2593,7 @@
      * @param flags Provides additional operating flags.
      */
     public final void requestShowSelf(int flags) {
-        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this,
+        ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
                 null /* icProto */);
         mPrivOps.showMySoftInput(flags);
     }
@@ -3298,67 +3280,91 @@
     }
 
     /**
-     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
-     * permission to the content.
+     * Used to inject custom {@link InputMethodServiceInternal}.
      *
-     * @param inputContentInfo Content to be temporarily exposed from the input method to the
-     * application.
-     * This cannot be {@code null}.
-     * @param inputConnection {@link InputConnection} with which
-     * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
-     * @hide
+     * @return the {@link InputMethodServiceInternal} to be used.
      */
+    @NonNull
     @Override
-    public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
-            @NonNull InputConnection inputConnection) {
-        if (inputConnection == null) {
-            return;
-        }
-        if (getCurrentInputConnection() != inputConnection) {
-            return;
-        }
-        exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @AnyThread
-    @Override
-    public final void notifyUserActionIfNecessary() {
-        synchronized (mLock) {
-            if (mNotifyUserActionSent) {
-                return;
+    final InputMethodServiceInternal createInputMethodServiceInternal() {
+        return new InputMethodServiceInternal() {
+            /**
+             * {@inheritDoc}
+             */
+            @NonNull
+            @Override
+            public Context getContext() {
+                return InputMethodService.this;
             }
-            mPrivOps.notifyUserActionAsync();
-            mNotifyUserActionSent = true;
-        }
-    }
 
-    /**
-     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
-     * permission to the content.
-     *
-     * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
-     * InputConnection)} for details.</p>
-     *
-     * @param inputContentInfo Content to be temporarily exposed from the input method to the
-     * application.
-     * This cannot be {@code null}.
-     * @param editorInfo The editor that receives {@link InputContentInfo}.
-     */
-    private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
-            @NonNull EditorInfo editorInfo) {
-        final Uri contentUri = inputContentInfo.getContentUri();
-        final IInputContentUriToken uriToken =
-                mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
-        if (uriToken == null) {
-            Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
-                    + " packageName=" + editorInfo.packageName);
-            return;
-        }
-        inputContentInfo.setUriToken(uriToken);
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void exposeContent(@NonNull InputContentInfo inputContentInfo,
+                    @NonNull InputConnection inputConnection) {
+                if (inputConnection == null) {
+                    return;
+                }
+                if (getCurrentInputConnection() != inputConnection) {
+                    return;
+                }
+                exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void notifyUserActionIfNecessary() {
+                synchronized (mLock) {
+                    if (mNotifyUserActionSent) {
+                        return;
+                    }
+                    mPrivOps.notifyUserActionAsync();
+                    mNotifyUserActionSent = true;
+                }
+            }
+
+            /**
+             * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+             * permission to the content.
+             *
+             * <p>See {@link #exposeContent(InputContentInfo, InputConnection)} for details.</p>
+             *
+             * @param inputContentInfo Content to be temporarily exposed from the input method to
+             *                         the application.  This cannot be {@code null}.
+             * @param editorInfo The editor that receives {@link InputContentInfo}.
+             */
+            private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
+                    @NonNull EditorInfo editorInfo) {
+                final Uri contentUri = inputContentInfo.getContentUri();
+                final IInputContentUriToken uriToken =
+                        mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
+                if (uriToken == null) {
+                    Log.e(TAG, "createInputContentAccessToken failed. contentUri="
+                            + contentUri.toString() + " packageName=" + editorInfo.packageName);
+                    return;
+                }
+                inputContentInfo.setUriToken(uriToken);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void dump(FileDescriptor fd, PrintWriter fout, String[]args) {
+                InputMethodService.this.dump(fd, fout, args);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void triggerServiceDump(String where, @Nullable byte[] icProto) {
+                ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
+            }
+        };
     }
 
     private int mapToImeWindowStatus() {
@@ -3428,42 +3434,44 @@
         p.println(" mSettingsObserver=" + mSettingsObserver);
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) {
-        final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
-        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);
+    private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto) {
+            final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
+            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(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));
+            if (icProto != null) {
+                proto.write(INPUT_CONNECTION_CALL, icProto);
+            }
+            proto.end(token);
         }
-        proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
-        proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
-        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));
-        if (icProto != null) {
-            proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
-        }
-        proto.end(token);
-    }
+    };
 }
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
new file mode 100644
index 0000000..7cd4ff6
--- /dev/null
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethodservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A set of internal methods exposed by {@link InputMethodService} to be called only from other
+ * framework classes for internal use.
+ *
+ * <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
+ */
+interface InputMethodServiceInternal {
+    /**
+     * @return {@link Context} associated with the service.
+     */
+    @NonNull
+    Context getContext();
+
+    /**
+     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+     * permission to the content.
+     *
+     * @param inputContentInfo Content to be temporarily exposed from the input method to the
+     *                         application. This cannot be {@code null}.
+     * @param inputConnection {@link InputConnection} with which
+     *                        {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}
+     *                        will be called.
+     */
+    default void exposeContent(@NonNull InputContentInfo inputContentInfo,
+            @NonNull InputConnection inputConnection) {
+    }
+
+    /**
+     * Called when the user took some actions that should be taken into consideration to update the
+     * MRU list for input method rotation.
+     */
+    default void notifyUserActionIfNecessary() {
+    }
+
+    /**
+     * Called when the system is asking the IME to dump its information for debugging.
+     *
+     * <p>The caller is responsible for checking {@link android.Manifest.permission.DUMP}.</p>
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param fout The file to which you should dump your state.  This will be
+     * closed for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    default void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+    }
+
+    /**
+     * Called with {@link com.android.internal.inputmethod.ImeTracing#triggerServiceDump(String,
+     * com.android.internal.inputmethod.ImeTracing.ServiceDumper, byte[])} needs to be triggered
+     * with the given parameters.
+     *
+     * @param where {@code where} parameter to be passed.
+     * @param icProto {@code icProto} parameter to be passed.
+     */
+    default void triggerServiceDump(String where, @Nullable byte[] icProto) {
+    }
+}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
deleted file mode 100644
index f352f05..0000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ /dev/null
@@ -1,470 +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 android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.CancellationGroup;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputConnectionWrapper;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread.
- *
- * <p>There are three types of per-client callbacks.</p>
- *
- * <ul>
- *     <li>{@link IInputMethodSession} - from the IME client</li>
- *     <li>{@link IMultiClientInputMethodSession} - from MultiClientInputMethodManagerService</li>
- *     <li>{@link InputChannel} - from the IME client</li>
- * </ul>
- *
- * <p>This class serializes all the incoming events among those channels onto
- * {@link MultiClientInputMethodServiceDelegate.ClientCallback} on the specified {@link Looper}
- * thread.</p>
- */
-final class MultiClientInputMethodClientCallbackAdaptor {
-    static final boolean DEBUG = false;
-    static final String TAG = MultiClientInputMethodClientCallbackAdaptor.class.getSimpleName();
-
-    private final Object mSessionLock = new Object();
-    @GuardedBy("mSessionLock")
-    CallbackImpl mCallbackImpl;
-    @GuardedBy("mSessionLock")
-    InputChannel mReadChannel;
-    @GuardedBy("mSessionLock")
-    KeyEvent.DispatcherState mDispatcherState;
-    @GuardedBy("mSessionLock")
-    Handler mHandler;
-    @GuardedBy("mSessionLock")
-    @Nullable
-    InputEventReceiver mInputEventReceiver;
-
-    private final CancellationGroup mCancellationGroup = new CancellationGroup();
-
-    IInputMethodSession.Stub createIInputMethodSession() {
-        synchronized (mSessionLock) {
-            return new InputMethodSessionImpl(
-                    mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
-        }
-    }
-
-    IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() {
-        synchronized (mSessionLock) {
-            return new MultiClientInputMethodSessionImpl(
-                    mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
-        }
-    }
-
-    MultiClientInputMethodClientCallbackAdaptor(
-            MultiClientInputMethodServiceDelegate.ClientCallback clientCallback, Looper looper,
-            KeyEvent.DispatcherState dispatcherState, InputChannel readChannel) {
-        synchronized (mSessionLock) {
-            mCallbackImpl = new CallbackImpl(this, clientCallback);
-            mDispatcherState = dispatcherState;
-            mHandler = new Handler(looper, null, true);
-            mReadChannel = readChannel;
-            mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(),
-                    mCancellationGroup, mDispatcherState, mCallbackImpl.mOriginalCallback);
-        }
-    }
-
-    private static final class KeyEventCallbackAdaptor implements KeyEvent.Callback {
-        private final MultiClientInputMethodServiceDelegate.ClientCallback mLocalCallback;
-
-        KeyEventCallbackAdaptor(
-                MultiClientInputMethodServiceDelegate.ClientCallback callback) {
-            mLocalCallback = callback;
-        }
-
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            return mLocalCallback.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-            return mLocalCallback.onKeyLongPress(keyCode, event);
-        }
-
-        @Override
-        public boolean onKeyUp(int keyCode, KeyEvent event) {
-            return mLocalCallback.onKeyUp(keyCode, event);
-        }
-
-        @Override
-        public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
-            return mLocalCallback.onKeyMultiple(keyCode, event);
-        }
-    }
-
-    private static final class ImeInputEventReceiver extends InputEventReceiver {
-        private final CancellationGroup mCancellationGroupOnFinishSession;
-        private final KeyEvent.DispatcherState mDispatcherState;
-        private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback;
-        private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor;
-
-        ImeInputEventReceiver(InputChannel readChannel, Looper looper,
-                CancellationGroup cancellationGroupOnFinishSession,
-                KeyEvent.DispatcherState dispatcherState,
-                MultiClientInputMethodServiceDelegate.ClientCallback callback) {
-            super(readChannel, looper);
-            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
-            mDispatcherState = dispatcherState;
-            mClientCallback = callback;
-            mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback);
-        }
-
-        @Override
-        public void onInputEvent(InputEvent event) {
-            if (mCancellationGroupOnFinishSession.isCanceled()) {
-                // The session has been finished.
-                finishInputEvent(event, false);
-                return;
-            }
-            boolean handled = false;
-            try {
-                if (event instanceof KeyEvent) {
-                    final KeyEvent keyEvent = (KeyEvent) event;
-                    handled = keyEvent.dispatch(mKeyEventCallbackAdaptor, mDispatcherState,
-                            mKeyEventCallbackAdaptor);
-                } else {
-                    final MotionEvent motionEvent = (MotionEvent) event;
-                    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
-                        handled = mClientCallback.onTrackballEvent(motionEvent);
-                    } else {
-                        handled = mClientCallback.onGenericMotionEvent(motionEvent);
-                    }
-                }
-            } finally {
-                finishInputEvent(event, handled);
-            }
-        }
-    }
-
-    private static final class InputMethodSessionImpl extends IInputMethodSession.Stub {
-        private final Object mSessionLock;
-        @GuardedBy("mSessionLock")
-        private CallbackImpl mCallbackImpl;
-        @GuardedBy("mSessionLock")
-        private Handler mHandler;
-        private final CancellationGroup mCancellationGroupOnFinishSession;
-
-        InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler,
-                CancellationGroup cancellationGroupOnFinishSession) {
-            mSessionLock = lock;
-            mCallbackImpl = callback;
-            mHandler = handler;
-            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
-        }
-
-        @Override
-        public void updateExtractedText(int token, ExtractedText text) {
-            reportNotSupported();
-        }
-
-        @Override
-        public void updateSelection(int oldSelStart, int oldSelEnd,
-                int newSelStart, int newSelEnd,
-                int candidatesStart, int candidatesEnd) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                final SomeArgs args = SomeArgs.obtain();
-                args.argi1 = oldSelStart;
-                args.argi2 = oldSelEnd;
-                args.argi3 = newSelStart;
-                args.argi4 = newSelEnd;
-                args.argi5 = candidatesStart;
-                args.argi6 = candidatesEnd;
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::updateSelection, mCallbackImpl, args));
-            }
-        }
-
-        @Override
-        public void viewClicked(boolean focusChanged) {
-            reportNotSupported();
-        }
-
-        @Override
-        public void updateCursor(Rect newCursor) {
-            reportNotSupported();
-        }
-
-        @Override
-        public void displayCompletions(CompletionInfo[] completions) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::displayCompletions, mCallbackImpl, completions));
-            }
-        }
-
-        @Override
-        public void appPrivateCommand(String action, Bundle data) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::appPrivateCommand, mCallbackImpl, action, data));
-            }
-        }
-
-        @Override
-        public void finishSession() {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mCancellationGroupOnFinishSession.cancelAll();
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::finishSession, mCallbackImpl));
-                mCallbackImpl = null;
-                mHandler = null;
-            }
-        }
-
-        @Override
-        public void updateCursorAnchorInfo(CursorAnchorInfo info) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info));
-            }
-        }
-
-        @Override
-        public final void notifyImeHidden() {
-            // no-op for multi-session since IME is responsible controlling navigation bar buttons.
-            reportNotSupported();
-        }
-
-        @Override
-        public void removeImeSurface() {
-            // no-op for multi-session
-            reportNotSupported();
-        }
-
-        @Override
-        public void finishInput() throws RemoteException {
-            // no-op for multi-session
-            reportNotSupported();
-        }
-    }
-
-    private static final class MultiClientInputMethodSessionImpl
-            extends IMultiClientInputMethodSession.Stub {
-        private final Object mSessionLock;
-        @GuardedBy("mSessionLock")
-        private CallbackImpl mCallbackImpl;
-        @GuardedBy("mSessionLock")
-        private Handler mHandler;
-        private final CancellationGroup mCancellationGroupOnFinishSession;
-
-        MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback,
-                Handler handler, CancellationGroup cancellationGroupOnFinishSession) {
-            mSessionLock = lock;
-            mCallbackImpl = callback;
-            mHandler = handler;
-            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
-        }
-
-        @Override
-        public void startInputOrWindowGainedFocus(@Nullable IInputContext inputContext,
-                int missingMethods, @Nullable EditorInfo editorInfo, int controlFlags,
-                @SoftInputModeFlags int softInputMode, int windowHandle) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                final SomeArgs args = SomeArgs.obtain();
-                // TODO(Bug 119211536): Remove dependency on AbstractInputMethodService from ICW
-                final WeakReference<AbstractInputMethodService> fakeIMS =
-                        new WeakReference<>(null);
-                args.arg1 = (inputContext == null) ? null
-                        : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods,
-                                mCancellationGroupOnFinishSession);
-                args.arg2 = editorInfo;
-                args.argi1 = controlFlags;
-                args.argi2 = softInputMode;
-                args.argi3 = windowHandle;
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::startInputOrWindowGainedFocus, mCallbackImpl, args));
-            }
-        }
-
-        @Override
-        public void showSoftInput(int flags, ResultReceiver resultReceiver) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::showSoftInput, mCallbackImpl, flags,
-                        resultReceiver));
-            }
-        }
-
-        @Override
-        public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
-            synchronized (mSessionLock) {
-                if (mCallbackImpl == null || mHandler == null) {
-                    return;
-                }
-                mHandler.sendMessage(PooledLambda.obtainMessage(
-                        CallbackImpl::hideSoftInput, mCallbackImpl, flags,
-                        resultReceiver));
-            }
-        }
-    }
-
-    /**
-     * The maim part of adaptor to {@link MultiClientInputMethodServiceDelegate.ClientCallback}.
-     */
-    @WorkerThread
-    private static final class CallbackImpl {
-        private final MultiClientInputMethodClientCallbackAdaptor mCallbackAdaptor;
-        private final MultiClientInputMethodServiceDelegate.ClientCallback mOriginalCallback;
-        private boolean mFinished = false;
-
-        CallbackImpl(MultiClientInputMethodClientCallbackAdaptor callbackAdaptor,
-                MultiClientInputMethodServiceDelegate.ClientCallback callback) {
-            mCallbackAdaptor = callbackAdaptor;
-            mOriginalCallback = callback;
-        }
-
-        void updateSelection(SomeArgs args) {
-            try {
-                if (mFinished) {
-                    return;
-                }
-                mOriginalCallback.onUpdateSelection(args.argi1, args.argi2, args.argi3,
-                        args.argi4, args.argi5, args.argi6);
-            } finally {
-                args.recycle();
-            }
-        }
-
-        void displayCompletions(CompletionInfo[] completions) {
-            if (mFinished) {
-                return;
-            }
-            mOriginalCallback.onDisplayCompletions(completions);
-        }
-
-        void appPrivateCommand(String action, Bundle data) {
-            if (mFinished) {
-                return;
-            }
-            mOriginalCallback.onAppPrivateCommand(action, data);
-        }
-
-        void finishSession() {
-            if (mFinished) {
-                return;
-            }
-            mFinished = true;
-            mOriginalCallback.onFinishSession();
-            synchronized (mCallbackAdaptor.mSessionLock) {
-                mCallbackAdaptor.mDispatcherState = null;
-                if (mCallbackAdaptor.mReadChannel != null) {
-                    mCallbackAdaptor.mReadChannel.dispose();
-                    mCallbackAdaptor.mReadChannel = null;
-                }
-                mCallbackAdaptor.mInputEventReceiver = null;
-            }
-        }
-
-        void updateCursorAnchorInfo(CursorAnchorInfo info) {
-            if (mFinished) {
-                return;
-            }
-            mOriginalCallback.onUpdateCursorAnchorInfo(info);
-        }
-
-        void startInputOrWindowGainedFocus(SomeArgs args) {
-            try {
-                if (mFinished) {
-                    return;
-                }
-                final InputConnectionWrapper inputConnection = (InputConnectionWrapper) args.arg1;
-                final EditorInfo editorInfo = (EditorInfo) args.arg2;
-                final int startInputFlags = args.argi1;
-                final int softInputMode = args.argi2;
-                final int windowHandle = args.argi3;
-                mOriginalCallback.onStartInputOrWindowGainedFocus(inputConnection, editorInfo,
-                        startInputFlags, softInputMode, windowHandle);
-            } finally {
-                args.recycle();
-            }
-        }
-
-        void showSoftInput(int flags, ResultReceiver resultReceiver) {
-            if (mFinished) {
-                return;
-            }
-            mOriginalCallback.onShowSoftInput(flags, resultReceiver);
-        }
-
-        void hideSoftInput(int flags, ResultReceiver resultReceiver) {
-            if (mFinished) {
-                return;
-            }
-            mOriginalCallback.onHideSoftInput(flags, resultReceiver);
-        }
-    }
-
-    private static void reportNotSupported() {
-        if (DEBUG) {
-            Log.d(TAG, Debug.getCaller() + " is not supported");
-        }
-    }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
deleted file mode 100644
index 0a23165..0000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
+++ /dev/null
@@ -1,378 +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 android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-import com.android.internal.inputmethod.StartInputFlags;
-
-/**
- * Defines all the public APIs and interfaces that are necessary to implement multi-client IMEs.
- *
- * <p>Actual implementation is further delegated to
- * {@link MultiClientInputMethodServiceDelegateImpl}.</p>
- *
- * @hide
- */
-public final class MultiClientInputMethodServiceDelegate {
-    // @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "android.inputmethodservice.MultiClientInputMethodService";
-
-    /**
-     * Special value that is guaranteed to be not used for IME client ID.
-     */
-    public static final int INVALID_CLIENT_ID = -1;
-
-    /**
-     * Special value that is guaranteed to be not used for window handle.
-     */
-    public static final int INVALID_WINDOW_HANDLE = -1;
-
-    private final MultiClientInputMethodServiceDelegateImpl mImpl;
-
-    /**
-     * Top-level callbacks for this {@link MultiClientInputMethodServiceDelegate}.
-     */
-    public interface ServiceCallback {
-        /**
-         * Called when this {@link MultiClientInputMethodServiceDelegate} is recognized by the
-         * system and privileged operations like {@link #createInputMethodWindowToken(int)} are
-         * ready to be called.
-         */
-        void initialized();
-
-        /**
-         * Called when a new IME client is recognized by the system.
-         *
-         * <p>Once the IME receives this callback, the IME can start interacting with the IME client
-         * by calling {@link #acceptClient(int, ClientCallback, KeyEvent.DispatcherState, Looper)}.
-         * </p>
-         *
-         * @param clientId ID of the client.
-         * @param uid UID of the IME client.
-         * @param pid PID of the IME client.
-         * @param selfReportedDisplayId display ID reported from the IME client. Since the system
-         *        does not validate this display ID, and at any time the IME client can lose the
-         *        access to this display ID, the IME needs to call
-         *        {@link #isUidAllowedOnDisplay(int, int)} to check whether the IME client still
-         *        has access to this display or not.
-         */
-        void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
-
-        /**
-         * Called when an IME client is being destroyed.
-         *
-         * @param clientId ID of the client.
-         */
-        void removeClient(int clientId);
-    }
-
-    /**
-     * Per-client callbacks.
-     */
-    public interface ClientCallback {
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#sendAppPrivateCommand(View, String, Bundle)}.
-         *
-         * @param action Name of the command to be performed.
-         * @param data Any data to include with the command.
-         * @see android.inputmethodservice.InputMethodService#onAppPrivateCommand(String, Bundle)
-         */
-        void onAppPrivateCommand(String action, Bundle data);
-
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#displayCompletions(View, CompletionInfo[])}.
-         *
-         * @param completions Completion information provided from the IME client.
-         * @see android.inputmethodservice.InputMethodService#onDisplayCompletions(CompletionInfo[])
-         */
-        void onDisplayCompletions(CompletionInfo[] completions);
-
-        /**
-         * Called when this callback session is closed. No further callback should not happen on
-         * this callback object.
-         */
-        void onFinishSession();
-
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int)} or
-         * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int,
-         * ResultReceiver)}.
-         *
-         * @param flags The flag passed by the client.
-         * @param resultReceiver The {@link ResultReceiver} passed by the client.
-         * @see android.inputmethodservice.InputMethodService#onWindowHidden()
-         */
-        void onHideSoftInput(int flags, ResultReceiver resultReceiver);
-
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or {@link
-         * android.view.inputmethod.InputMethodManager#showSoftInput(View, int, ResultReceiver)}.
-         *
-         * @param flags The flag passed by the client.
-         * @param resultReceiver The {@link ResultReceiver} passed by the client.
-         * @see android.inputmethodservice.InputMethodService#onWindowShown()
-         */
-        void onShowSoftInput(int flags, ResultReceiver resultReceiver);
-
-        /**
-         * A generic callback when {@link InputConnection} is being established.
-         *
-         * @param inputConnection The {@link InputConnection} to be established.
-         * @param editorInfo The {@link EditorInfo} reported from the IME client.
-         * @param startInputFlags Any combinations of {@link StartInputFlags}.
-         * @param softInputMode SoftWindowMode specified to this window.
-         * @param targetWindowHandle A unique Window token.
-         * @see android.inputmethodservice.InputMethodService#onStartInput(EditorInfo, boolean)
-         */
-        void onStartInputOrWindowGainedFocus(
-                @Nullable InputConnection inputConnection,
-                @Nullable EditorInfo editorInfo,
-                @StartInputFlags int startInputFlags,
-                @SoftInputModeFlags int softInputMode,
-                int targetWindowHandle);
-
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#updateCursorAnchorInfo(View,
-         * CursorAnchorInfo)}.
-         *
-         * @param info The {@link CursorAnchorInfo} passed by the client.
-         * @see android.inputmethodservice.InputMethodService#onUpdateCursorAnchorInfo(
-         *      CursorAnchorInfo)
-         */
-        void onUpdateCursorAnchorInfo(CursorAnchorInfo info);
-
-        /**
-         * Called when the associated IME client called {@link
-         * android.view.inputmethod.InputMethodManager#updateSelection(View, int, int, int, int)}.
-         *
-         * @param oldSelStart The previous selection start index.
-         * @param oldSelEnd The previous selection end index.
-         * @param newSelStart The new selection start index.
-         * @param newSelEnd The new selection end index.
-         * @param candidatesStart The new candidate start index.
-         * @param candidatesEnd The new candidate end index.
-         * @see android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
-         *      int, int)
-         */
-        void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
-                int candidatesStart, int candidatesEnd);
-
-        /**
-         * Called to give a chance for the IME to intercept generic motion events before they are
-         * processed by the application.
-         *
-         * @param event {@link MotionEvent} that is about to be handled by the IME client.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onGenericMotionEvent(MotionEvent)
-         */
-        boolean onGenericMotionEvent(MotionEvent event);
-
-        /**
-         * Called to give a chance for the IME to intercept key down events before they are
-         * processed by the application.
-         *
-         * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
-         * @param event {@link KeyEvent} for this key down event.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent)
-         */
-        boolean onKeyDown(int keyCode, KeyEvent event);
-
-        /**
-         * Called to give a chance for the IME to intercept key long press events before they are
-         * processed by the application.
-         *
-         * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
-         * @param event {@link KeyEvent} for this key long press event.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onKeyLongPress(int, KeyEvent)
-         */
-        boolean onKeyLongPress(int keyCode, KeyEvent event);
-
-        /**
-         * Called to give a chance for the IME to intercept key multiple events before they are
-         * processed by the application.
-         *
-         * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
-         * @param event {@link KeyEvent} for this key multiple event.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onKeyMultiple(int, int, KeyEvent)
-         */
-        boolean onKeyMultiple(int keyCode, KeyEvent event);
-
-        /**
-         * Called to give a chance for the IME to intercept key up events before they are processed
-         * by the application.
-         *
-         * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
-         * @param event {@link KeyEvent} for this key up event.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent)
-         */
-        boolean onKeyUp(int keyCode, KeyEvent event);
-
-        /**
-         * Called to give a chance for the IME to intercept generic motion events before they are
-         * processed by the application.
-         *
-         * @param event {@link MotionEvent} that is about to be handled by the IME client.
-         * @return {@code true} to tell the IME client that the IME handled this event.
-         * @see android.inputmethodservice.InputMethodService#onTrackballEvent(MotionEvent)
-         */
-        boolean onTrackballEvent(MotionEvent event);
-    }
-
-    private MultiClientInputMethodServiceDelegate(Context context,
-            ServiceCallback serviceCallback) {
-        mImpl = new MultiClientInputMethodServiceDelegateImpl(context, serviceCallback);
-    }
-
-    /**
-     * Must be called by the multi-client IME implementer to create
-     * {@link MultiClientInputMethodServiceDelegate}.
-     *
-     * @param context {@link Context} with which the delegate should interact with the system.
-     * @param serviceCallback {@link ServiceCallback} to receive service-level callbacks.
-     * @return A new instance of {@link MultiClientInputMethodServiceDelegate}.
-     */
-    public static MultiClientInputMethodServiceDelegate create(Context context,
-            ServiceCallback serviceCallback) {
-        return new MultiClientInputMethodServiceDelegate(context, serviceCallback);
-    }
-
-    /**
-     * Must be called by the multi-client IME service when {@link android.app.Service#onDestroy()}
-     * is called.
-     */
-    public void onDestroy() {
-        mImpl.onDestroy();
-    }
-
-    /**
-     * Must be called by the multi-client IME service when
-     * {@link android.app.Service#onBind(Intent)} is called.
-     *
-     * @param intent {@link Intent} passed to {@link android.app.Service#onBind(Intent)}.
-     * @return An {@link IBinder} object that needs to be returned from
-     *         {@link android.app.Service#onBind(Intent)}.
-     */
-    public IBinder onBind(Intent intent) {
-        return mImpl.onBind(intent);
-    }
-
-    /**
-     * Must be called by the multi-client IME service when
-     * {@link android.app.Service#onUnbind(Intent)} is called.
-     *
-     * @param intent {@link Intent} passed to {@link android.app.Service#onUnbind(Intent)}.
-     * @return A boolean value that needs to be returned from
-     *         {@link android.app.Service#onUnbind(Intent)}.
-     */
-    public boolean onUnbind(Intent intent) {
-        return mImpl.onUnbind(intent);
-    }
-
-    /**
-     * Must be called by the multi-client IME service to create a special window token for IME
-     * window.
-     *
-     * <p>This method is available only after {@link ServiceCallback#initialized()}.</p>
-     *
-     * @param displayId display ID on which the IME window will be shown.
-     * @return Window token to be specified to the IME window/
-     */
-    public IBinder createInputMethodWindowToken(int displayId) {
-        return mImpl.createInputMethodWindowToken(displayId);
-    }
-
-    /**
-     * Must be called by the multi-client IME service to notify the system when the IME is ready to
-     * accept callback events from the specified IME client.
-     *
-     * @param clientId The IME client ID specified in
-     *                 {@link ServiceCallback#addClient(int, int, int, int)}.
-     * @param clientCallback The {@link ClientCallback} to receive callback events from this IME
-     *                       client.
-     * @param dispatcherState {@link KeyEvent.DispatcherState} to be used when receiving key-related
-     *                        callbacks in {@link ClientCallback}.
-     * @param looper {@link Looper} on which {@link ClientCallback} will be called back.
-     */
-    public void acceptClient(int clientId, ClientCallback clientCallback,
-            KeyEvent.DispatcherState dispatcherState, Looper looper) {
-        mImpl.acceptClient(clientId, clientCallback, dispatcherState, looper);
-    }
-
-    /**
-     * Must be called by the multi-client IME service to notify the system when the IME is ready to
-     * interact with the window in the IME client.
-     *
-     * @param clientId The IME client ID specified in
-     *                 {@link ServiceCallback#addClient(int, int, int, int)}.
-     * @param targetWindowHandle The window handle specified in
-     *                           {@link ClientCallback#onStartInputOrWindowGainedFocus}.
-     * @param imeWindowToken The IME window token returned from
-     *                       {@link #createInputMethodWindowToken(int)}.
-     */
-    public void reportImeWindowTarget(int clientId, int targetWindowHandle,
-            IBinder imeWindowToken) {
-        mImpl.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
-    }
-
-    /**
-     * Can be called by the multi-client IME service to check if the given {@code uid} is allowed
-     * to access to {@code displayId}.
-     *
-     * @param displayId Display ID to be queried.
-     * @param uid UID to be queried.
-     * @return {@code true} if {@code uid} is allowed to access to {@code displayId}.
-     */
-    public boolean isUidAllowedOnDisplay(int displayId, int uid) {
-        return mImpl.isUidAllowedOnDisplay(displayId, uid);
-    }
-
-    /**
-     * Can be called by MSIME to activate/deactivate a client when it is gaining/losing focus
-     * respectively.
-     *
-     * @param clientId client ID to activate/deactivate.
-     * @param active {@code true} to activate a client.
-     */
-    public void setActive(int clientId, boolean active) {
-        mImpl.setActive(clientId, active);
-    }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
deleted file mode 100644
index 04db8d6..0000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
+++ /dev/null
@@ -1,197 +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 android.inputmethodservice;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.KeyEvent;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.MultiClientInputMethodPrivilegedOperations;
-
-import java.lang.annotation.Retention;
-import java.lang.ref.WeakReference;
-
-final class MultiClientInputMethodServiceDelegateImpl {
-    private static final String TAG = "MultiClientInputMethodServiceDelegateImpl";
-
-    private final Object mLock = new Object();
-
-    @Retention(SOURCE)
-    @IntDef({InitializationPhase.INSTANTIATED,
-            InitializationPhase.ON_BIND_CALLED,
-            InitializationPhase.INITIALIZE_CALLED,
-            InitializationPhase.ON_UNBIND_CALLED,
-            InitializationPhase.ON_DESTROY_CALLED})
-    private @interface InitializationPhase {
-        int INSTANTIATED = 1;
-        int ON_BIND_CALLED = 2;
-        int INITIALIZE_CALLED = 3;
-        int ON_UNBIND_CALLED  = 4;
-        int ON_DESTROY_CALLED = 5;
-    }
-
-    @GuardedBy("mLock")
-    @InitializationPhase
-    private int mInitializationPhase;
-
-    private final MultiClientInputMethodPrivilegedOperations mPrivOps =
-            new MultiClientInputMethodPrivilegedOperations();
-
-    private final MultiClientInputMethodServiceDelegate.ServiceCallback mServiceCallback;
-
-    private final Context mContext;
-
-    MultiClientInputMethodServiceDelegateImpl(Context context,
-            MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
-        mInitializationPhase = InitializationPhase.INSTANTIATED;
-        mContext = context;
-        mServiceCallback = serviceCallback;
-    }
-
-    void onDestroy() {
-        synchronized (mLock) {
-            switch (mInitializationPhase) {
-                case InitializationPhase.INSTANTIATED:
-                case InitializationPhase.ON_UNBIND_CALLED:
-                    mInitializationPhase = InitializationPhase.ON_DESTROY_CALLED;
-                    break;
-                default:
-                    Log.e(TAG, "unexpected state=" + mInitializationPhase);
-                    break;
-            }
-        }
-    }
-
-    private static final class ServiceImpl extends IMultiClientInputMethod.Stub {
-        private final WeakReference<MultiClientInputMethodServiceDelegateImpl> mImpl;
-
-        ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
-            mImpl = new WeakReference<>(service);
-        }
-
-        @Override
-        public void initialize(IMultiClientInputMethodPrivilegedOperations privOps) {
-            final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
-            if (service == null) {
-                return;
-            }
-            synchronized (service.mLock) {
-                switch (service.mInitializationPhase) {
-                    case InitializationPhase.ON_BIND_CALLED:
-                        service.mPrivOps.set(privOps);
-                        service.mInitializationPhase = InitializationPhase.INITIALIZE_CALLED;
-                        service.mServiceCallback.initialized();
-                        break;
-                    default:
-                        Log.e(TAG, "unexpected state=" + service.mInitializationPhase);
-                        break;
-                }
-            }
-        }
-
-        @Override
-        public void addClient(int clientId, int uid, int pid, int selfReportedDisplayId) {
-            final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
-            if (service == null) {
-                return;
-            }
-            service.mServiceCallback.addClient(clientId, uid, pid, selfReportedDisplayId);
-        }
-
-        @Override
-        public void removeClient(int clientId) {
-            final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
-            if (service == null) {
-                return;
-            }
-            service.mServiceCallback.removeClient(clientId);
-        }
-    }
-
-    IBinder onBind(Intent intent) {
-        synchronized (mLock) {
-            switch (mInitializationPhase) {
-                case InitializationPhase.INSTANTIATED:
-                    mInitializationPhase = InitializationPhase.ON_BIND_CALLED;
-                    return new ServiceImpl(this);
-                default:
-                    Log.e(TAG, "unexpected state=" + mInitializationPhase);
-                    break;
-            }
-        }
-        return null;
-    }
-
-    boolean onUnbind(Intent intent) {
-        synchronized (mLock) {
-            switch (mInitializationPhase) {
-                case InitializationPhase.ON_BIND_CALLED:
-                case InitializationPhase.INITIALIZE_CALLED:
-                    mInitializationPhase = InitializationPhase.ON_UNBIND_CALLED;
-                    mPrivOps.dispose();
-                    break;
-                default:
-                    Log.e(TAG, "unexpected state=" + mInitializationPhase);
-                    break;
-            }
-        }
-        return false;
-    }
-
-    IBinder createInputMethodWindowToken(int displayId) {
-        return mPrivOps.createInputMethodWindowToken(displayId);
-    }
-
-    void acceptClient(int clientId,
-            MultiClientInputMethodServiceDelegate.ClientCallback clientCallback,
-            KeyEvent.DispatcherState dispatcherState, Looper looper) {
-        final InputChannel[] channels = InputChannel.openInputChannelPair("MSIMS-session");
-        final InputChannel writeChannel = channels[0];
-        final InputChannel readChannel = channels[1];
-        try {
-            final MultiClientInputMethodClientCallbackAdaptor callbackAdaptor =
-                    new MultiClientInputMethodClientCallbackAdaptor(clientCallback, looper,
-                            dispatcherState, readChannel);
-            mPrivOps.acceptClient(clientId, callbackAdaptor.createIInputMethodSession(),
-                    callbackAdaptor.createIMultiClientInputMethodSession(), writeChannel);
-        } finally {
-            writeChannel.dispose();
-        }
-    }
-
-    void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
-        mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
-    }
-
-    boolean isUidAllowedOnDisplay(int displayId, int uid) {
-        return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
-    }
-
-    void setActive(int clientId, boolean active) {
-        mPrivOps.setActive(clientId, active);
-    }
-}
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
new file mode 100644
index 0000000..5a8687d
--- /dev/null
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -0,0 +1,426 @@
+/*
+ * 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 android.inputmethodservice;
+
+import android.annotation.AnyThread;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+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.Completable;
+import com.android.internal.inputmethod.IInputContextInvoker;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputConnectionProtoDumper;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethod;
+
+import java.lang.ref.WeakReference;
+
+final class RemoteInputConnection implements InputConnection {
+    private static final String TAG = "RemoteInputConnection";
+
+    private static final int MAX_WAIT_TIME_MILLIS = 2000;
+
+    @NonNull
+    private final IInputContextInvoker mInvoker;
+
+    @NonNull
+    private final WeakReference<InputMethodServiceInternal> mInputMethodService;
+
+    @MissingMethodFlags
+    private final int mMissingMethods;
+
+    /**
+     * Signaled when the system decided to take away IME focus from the target app.
+     *
+     * <p>This is expected to be signaled immediately when the IME process receives
+     * {@link IInputMethod#unbindInput()}.</p>
+     */
+    @NonNull
+    private final CancellationGroup mCancellationGroup;
+
+    RemoteInputConnection(
+            @NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
+            IInputContext inputContext, @MissingMethodFlags int missingMethods,
+            @NonNull CancellationGroup cancellationGroup) {
+        mInputMethodService = inputMethodService;
+        mInvoker = IInputContextInvoker.create(inputContext);
+        mMissingMethods = missingMethods;
+        mCancellationGroup = cancellationGroup;
+    }
+
+    /**
+     * See {@link InputConnection#getTextAfterCursor(int, int)}.
+     */
+    @Nullable
+    @AnyThread
+    public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
+        if (length < 0 || mCancellationGroup.isCanceled()) {
+            return null;
+        }
+
+        final Completable.CharSequence value = mInvoker.getTextAfterCursor(length, flags);
+        final CharSequence result = Completable.getResultOrNull(
+                value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
+                    flags, result);
+            inputMethodService.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
+        }
+
+        return result;
+    }
+
+    /**
+     * See {@link InputConnection#getTextBeforeCursor(int, int)}.
+     */
+    @Nullable
+    @AnyThread
+    public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
+        if (length < 0 || mCancellationGroup.isCanceled()) {
+            return null;
+        }
+
+        final Completable.CharSequence value = mInvoker.getTextBeforeCursor(length, flags);
+        final CharSequence result = Completable.getResultOrNull(
+                value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
+                    flags, result);
+            inputMethodService.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
+        }
+
+        return result;
+    }
+
+    @AnyThread
+    public CharSequence getSelectedText(int flags) {
+        if (mCancellationGroup.isCanceled()) {
+            return null;
+        }
+
+        if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
+            // This method is not implemented.
+            return null;
+        }
+        final Completable.CharSequence value = mInvoker.getSelectedText(flags);
+        final CharSequence result = Completable.getResultOrNull(
+                value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
+                    result);
+            inputMethodService.triggerServiceDump(TAG + "#getSelectedText", icProto);
+        }
+
+        return result;
+    }
+
+    /**
+     * 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(
+            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+        if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) {
+            return null;
+        }
+
+        if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
+            // This method is not implemented.
+            return null;
+        }
+        final Completable.SurroundingText value = mInvoker.getSurroundingText(beforeLength,
+                afterLength, flags);
+        final SurroundingText result = Completable.getResultOrNull(
+                value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
+                    beforeLength, afterLength, flags, result);
+            inputMethodService.triggerServiceDump(TAG + "#getSurroundingText", icProto);
+        }
+
+        return result;
+    }
+
+    @AnyThread
+    public int getCursorCapsMode(int reqModes) {
+        if (mCancellationGroup.isCanceled()) {
+            return 0;
+        }
+
+        final Completable.Int value = mInvoker.getCursorCapsMode(reqModes);
+        final int result = Completable.getResultOrZero(
+                value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
+                    reqModes, result);
+            inputMethodService.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
+        }
+
+        return result;
+    }
+
+    @AnyThread
+    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        if (mCancellationGroup.isCanceled()) {
+            return null;
+        }
+
+        final Completable.ExtractedText value = mInvoker.getExtractedText(request, flags);
+        final ExtractedText result = Completable.getResultOrNull(
+                value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+            final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
+                    request, flags, result);
+            inputMethodService.triggerServiceDump(TAG + "#getExtractedText", icProto);
+        }
+
+        return result;
+    }
+
+    @AnyThread
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        final boolean handled = mInvoker.commitText(text, newCursorPosition);
+        if (handled) {
+            notifyUserActionIfNecessary();
+        }
+        return handled;
+    }
+
+    @AnyThread
+    private void notifyUserActionIfNecessary() {
+        final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+        if (inputMethodService == null) {
+            // This basically should not happen, because it's the the caller of this method.
+            return;
+        }
+        inputMethodService.notifyUserActionIfNecessary();
+    }
+
+    @AnyThread
+    public boolean commitCompletion(CompletionInfo text) {
+        return mInvoker.commitCompletion(text);
+    }
+
+    @AnyThread
+    public boolean commitCorrection(CorrectionInfo correctionInfo) {
+        if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
+            // This method is not implemented.
+            return false;
+        }
+        return mInvoker.commitCorrection(correctionInfo);
+    }
+
+    @AnyThread
+    public boolean setSelection(int start, int end) {
+        return mInvoker.setSelection(start, end);
+    }
+
+    @AnyThread
+    public boolean performEditorAction(int actionCode) {
+        return mInvoker.performEditorAction(actionCode);
+    }
+
+    @AnyThread
+    public boolean performContextMenuAction(int id) {
+        return mInvoker.performContextMenuAction(id);
+    }
+
+    @AnyThread
+    public boolean setComposingRegion(int start, int end) {
+        if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
+            // This method is not implemented.
+            return false;
+        }
+        return mInvoker.setComposingRegion(start, end);
+    }
+
+    @AnyThread
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
+        if (handled) {
+            notifyUserActionIfNecessary();
+        }
+        return handled;
+    }
+
+    @AnyThread
+    public boolean finishComposingText() {
+        return mInvoker.finishComposingText();
+    }
+
+    @AnyThread
+    public boolean beginBatchEdit() {
+        return mInvoker.beginBatchEdit();
+    }
+
+    @AnyThread
+    public boolean endBatchEdit() {
+        return mInvoker.endBatchEdit();
+    }
+
+    @AnyThread
+    public boolean sendKeyEvent(KeyEvent event) {
+        final boolean handled = mInvoker.sendKeyEvent(event);
+        if (handled) {
+            notifyUserActionIfNecessary();
+        }
+        return handled;
+    }
+
+    @AnyThread
+    public boolean clearMetaKeyStates(int states) {
+        return mInvoker.clearMetaKeyStates(states);
+    }
+
+    @AnyThread
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        return mInvoker.deleteSurroundingText(beforeLength, afterLength);
+    }
+
+    @AnyThread
+    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+        if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
+            // This method is not implemented.
+            return false;
+        }
+        return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+    }
+
+    @AnyThread
+    public boolean reportFullscreenMode(boolean enabled) {
+        // Nothing should happen when called from input method.
+        return false;
+    }
+
+    @AnyThread
+    public boolean performSpellCheck() {
+        return mInvoker.performSpellCheck();
+    }
+
+    @AnyThread
+    public boolean performPrivateCommand(String action, Bundle data) {
+        return mInvoker.performPrivateCommand(action, data);
+    }
+
+    @AnyThread
+    public boolean requestCursorUpdates(int cursorUpdateMode) {
+        if (mCancellationGroup.isCanceled()) {
+            return false;
+        }
+
+        if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
+            // This method is not implemented.
+            return false;
+        }
+        final Completable.Boolean value = mInvoker.requestCursorUpdates(cursorUpdateMode);
+        return Completable.getResultOrFalse(value, TAG, "requestCursorUpdates()",
+                mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+    }
+
+    @AnyThread
+    public Handler getHandler() {
+        // Nothing should happen when called from input method.
+        return null;
+    }
+
+    @AnyThread
+    public void closeConnection() {
+        // Nothing should happen when called from input method.
+    }
+
+    @AnyThread
+    public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+        if (mCancellationGroup.isCanceled()) {
+            return false;
+        }
+
+        if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
+            // This method is not implemented.
+            return false;
+        }
+
+        if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+            final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
+            if (inputMethodService == null) {
+                // This basically should not happen, because it's the caller of this method.
+                return false;
+            }
+            inputMethodService.exposeContent(inputContentInfo, this);
+        }
+
+        final Completable.Boolean value = mInvoker.commitContent(inputContentInfo, flags, opts);
+        return Completable.getResultOrFalse(
+                value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
+    }
+
+    /**
+     * See {@link InputConnection#setImeConsumesInput(boolean)}.
+     */
+    @AnyThread
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
+        return mInvoker.setImeConsumesInput(imeConsumesInput);
+    }
+
+    @AnyThread
+    private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
+        return (mMissingMethods & methodFlag) == methodFlag;
+    }
+
+    @AnyThread
+    @Override
+    public String toString() {
+        return "RemoteInputConnection{idHash=#"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " mMissingMethods="
+                + InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
+    }
+}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 3bde6fa..1d07a03 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -26,8 +26,10 @@
 import android.telephony.Annotation.NetworkType;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
+import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -121,11 +123,37 @@
         }
         builder.append(", metered=").append(mMetered);
         builder.append(", defaultNetwork=").append(mDefaultNetwork);
-        // TODO(180557699): Print a human readable string for OEM managed state.
-        builder.append(", oemManaged=").append(mOemManaged);
+        builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
         return builder.append("}").toString();
     }
 
+    /**
+     * Get the human readable representation of a bitfield representing the OEM managed state of a
+     * network.
+     */
+    static String getOemManagedNames(int oemManaged) {
+        if (oemManaged == OEM_NONE) {
+            return "OEM_NONE";
+        }
+        final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+        final ArrayList<String> oemManagedNames = new ArrayList<String>();
+        for (int position : bitPositions) {
+            oemManagedNames.add(nameOfOemManaged(1 << position));
+        }
+        return String.join(",", oemManagedNames);
+    }
+
+    private static String nameOfOemManaged(int oemManagedBit) {
+        switch (oemManagedBit) {
+            case OEM_PAID:
+                return "OEM_PAID";
+            case OEM_PRIVATE:
+                return "OEM_PRIVATE";
+            default:
+                return "Invalid(" + oemManagedBit + ")";
+        }
+    }
+
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 288b06e..46c83df 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1581,32 +1583,37 @@
                 // processes this every time device has transmitted/received amount equivalent to
                 // global threshold alert (~ 2MB) across all interfaces.
                 final long rxBytesAcrossUnderlyingIfaces =
-                        underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
+                        multiplySafeByRational(underlyingIfacesTotal.rxBytes,
+                                rxBytes[i], tunIfaceTotal.rxBytes);
                 // app must not be blamed for more than it consumed on tunIface
                 totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
             }
             long totalRxPackets = 0;
             if (tunIfaceTotal.rxPackets > 0) {
                 final long rxPacketsAcrossUnderlyingIfaces =
-                        underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
+                        multiplySafeByRational(underlyingIfacesTotal.rxPackets,
+                                rxPackets[i], tunIfaceTotal.rxPackets);
                 totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
             }
             long totalTxBytes = 0;
             if (tunIfaceTotal.txBytes > 0) {
                 final long txBytesAcrossUnderlyingIfaces =
-                        underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
+                        multiplySafeByRational(underlyingIfacesTotal.txBytes,
+                                txBytes[i], tunIfaceTotal.txBytes);
                 totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
             }
             long totalTxPackets = 0;
             if (tunIfaceTotal.txPackets > 0) {
                 final long txPacketsAcrossUnderlyingIfaces =
-                        underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
+                        multiplySafeByRational(underlyingIfacesTotal.txPackets,
+                                txPackets[i], tunIfaceTotal.txPackets);
                 totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
             }
             long totalOperations = 0;
             if (tunIfaceTotal.operations > 0) {
                 final long operationsAcrossUnderlyingIfaces =
-                        underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
+                        multiplySafeByRational(underlyingIfacesTotal.operations,
+                                operations[i], tunIfaceTotal.operations);
                 totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
             }
             // In a second pass, distribute these values across interfaces in the proportion that
@@ -1618,37 +1625,37 @@
                 tmpEntry.set = set[i];
                 if (underlyingIfacesTotal.rxBytes > 0) {
                     tmpEntry.rxBytes =
-                            totalRxBytes
-                                    * perInterfaceTotal[j].rxBytes
-                                    / underlyingIfacesTotal.rxBytes;
+                            multiplySafeByRational(totalRxBytes,
+                                    perInterfaceTotal[j].rxBytes,
+                                    underlyingIfacesTotal.rxBytes);
                 }
                 tmpEntry.rxPackets = 0;
                 if (underlyingIfacesTotal.rxPackets > 0) {
                     tmpEntry.rxPackets =
-                            totalRxPackets
-                                    * perInterfaceTotal[j].rxPackets
-                                    / underlyingIfacesTotal.rxPackets;
+                            multiplySafeByRational(totalRxPackets,
+                                    perInterfaceTotal[j].rxPackets,
+                                    underlyingIfacesTotal.rxPackets);
                 }
                 tmpEntry.txBytes = 0;
                 if (underlyingIfacesTotal.txBytes > 0) {
                     tmpEntry.txBytes =
-                            totalTxBytes
-                                    * perInterfaceTotal[j].txBytes
-                                    / underlyingIfacesTotal.txBytes;
+                            multiplySafeByRational(totalTxBytes,
+                                    perInterfaceTotal[j].txBytes,
+                                    underlyingIfacesTotal.txBytes);
                 }
                 tmpEntry.txPackets = 0;
                 if (underlyingIfacesTotal.txPackets > 0) {
                     tmpEntry.txPackets =
-                            totalTxPackets
-                                    * perInterfaceTotal[j].txPackets
-                                    / underlyingIfacesTotal.txPackets;
+                            multiplySafeByRational(totalTxPackets,
+                                    perInterfaceTotal[j].txPackets,
+                                    underlyingIfacesTotal.txPackets);
                 }
                 tmpEntry.operations = 0;
                 if (underlyingIfacesTotal.operations > 0) {
                     tmpEntry.operations =
-                            totalOperations
-                                    * perInterfaceTotal[j].operations
-                                    / underlyingIfacesTotal.operations;
+                            multiplySafeByRational(totalOperations,
+                                    perInterfaceTotal[j].operations,
+                                    underlyingIfacesTotal.operations);
                 }
                 // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying
                 // interface. Add that data usage to this object.
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 249154a..68917a8 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -427,7 +427,7 @@
             builder.append(", subType=").append(mSubType);
         }
         if (mOemManaged != OEM_MANAGED_ALL) {
-            builder.append(", oemManaged=").append(mOemManaged);
+            builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
         }
         builder.append(", subscriberIdMatchRule=")
                 .append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
@@ -781,6 +781,19 @@
         }
     }
 
+    private static String getOemManagedNames(int oemManaged) {
+        switch (oemManaged) {
+            case OEM_MANAGED_ALL:
+                return "OEM_MANAGED_ALL";
+            case OEM_MANAGED_NO:
+                return "OEM_MANAGED_NO";
+            case OEM_MANAGED_YES:
+                return "OEM_MANAGED_YES";
+            default:
+                return NetworkIdentity.getOemManagedNames(oemManaged);
+        }
+    }
+
     /**
      * Examine the given template and normalize if it refers to a "merged"
      * mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fa3ff8a..2ced056 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -529,7 +529,7 @@
 
         /**
          * Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
-         * and it is possible that some apps will ignore it.
+         * and it is possible that some apps will ignore it. PAC proxies are not supported.
          */
         @NonNull
         public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) {
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -3,5 +3,5 @@
 benedictwong@google.com
 ckesting@google.com
 evitayan@google.com
+junyin@google.com
 nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 2c088e2..7f526c1 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -262,7 +262,7 @@
     /**
      * Indicates that a new wifi scan has started.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
@@ -276,7 +276,7 @@
     /**
      * Indicates that an ongoing wifi scan has stopped.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -290,7 +290,7 @@
     /**
      * Indicates that a new wifi batched scan has started.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      * @param csph Channels scanned per hour.
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
@@ -306,7 +306,7 @@
     /**
      * Indicates that an ongoing wifi batched scan has stopped.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -322,7 +322,9 @@
      *
      * @return Instance of {@link CellularBatteryStats}.
      */
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
     public @NonNull CellularBatteryStats getCellularBatteryStats() {
         try {
             return mBatteryStats.getCellularBatteryStats();
@@ -337,7 +339,9 @@
      *
      * @return Instance of {@link WifiBatteryStats}.
      */
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
     public @NonNull WifiBatteryStats getWifiBatteryStats() {
         try {
             return mBatteryStats.getWifiBatteryStats();
@@ -350,7 +354,7 @@
     /**
      * Indicates an app acquiring full wifi lock.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
@@ -364,7 +368,7 @@
     /**
      * Indicates an app releasing full wifi lock.
      *
-     * @param ws Worksource (to be used for battery blaming).
+     * @param ws worksource (to be used for battery blaming).
      */
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
@@ -479,6 +483,63 @@
         }
     }
 
+    /**
+     * Indicates that a new Bluetooth LE scan has started.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param isUnoptimized whether or not the scan has a filter.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanStarted(@NonNull WorkSource ws, boolean isUnoptimized) {
+        try {
+            mBatteryStats.noteBleScanStarted(ws, isUnoptimized);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an ongoing Bluetooth LE scan has stopped.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param isUnoptimized whether or not the scan has a filter.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanStopped(@NonNull WorkSource ws, boolean isUnoptimized) {
+        try {
+            mBatteryStats.noteBleScanStopped(ws, isUnoptimized);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that Bluetooth LE has been reset.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanReset() {
+        try {
+            mBatteryStats.noteBleScanReset();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that Bluetooth LE scan has received new results.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param numNewResults number of results received since last update.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanResults(@NonNull WorkSource ws, int numNewResults) {
+        try {
+            mBatteryStats.noteBleScanResults(ws, numNewResults);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     private static int getDataConnectionPowerState(boolean isActive) {
         // TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
         // for both Wifi and mobile. It would make more sense to separate the constants to a
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f483752..b128f68 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -82,8 +82,6 @@
 
     public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
 
-    private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
-
     // XML tags and attributes for BatteryUsageStats persistence
     static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
     static final String XML_TAG_AGGREGATE = "aggregate";
@@ -112,6 +110,8 @@
     static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
     static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
 
+    private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+
     private final int mDischargePercentage;
     private final double mBatteryCapacityMah;
     private final long mStatsStartTimestampMs;
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d44b016..2afbe8b 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -53,6 +53,12 @@
 
     private static volatile Binder.ProxyTransactListener sTransactListener = null;
 
+    private static class BinderProxyMapSizeException extends AssertionError {
+        BinderProxyMapSizeException(String s) {
+            super(s);
+        }
+    };
+
     /**
      * @see {@link Binder#setProxyTransactListener(listener)}.
      */
@@ -73,7 +79,10 @@
         private static final int LOG_MAIN_INDEX_SIZE = 8;
         private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
         private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
-        // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+        /**
+         * Debuggable builds will throw an BinderProxyMapSizeException if the number of
+         * map entries exceeds:
+         */
         private static final int CRASH_AT_SIZE = 20_000;
 
         /**
@@ -228,7 +237,8 @@
                         dumpProxyInterfaceCounts();
                         dumpPerUidProxyCounts();
                         Runtime.getRuntime().gc();
-                        throw new AssertionError("Binder ProxyMap has too many entries: "
+                        throw new BinderProxyMapSizeException(
+                                "Binder ProxyMap has too many entries: "
                                 + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
                                 + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
                     } else if (totalSize > 3 * totalUnclearedSize / 2) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bf394d..394d270 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1130,6 +1130,11 @@
          * S.
          */
         public static final int S = 31;
+
+        /**
+         * Tiramisu.
+         */
+        public static final int TIRAMISU = CUR_DEVELOPMENT;
     }
 
     /** The type of build, like "user" or "eng". */
@@ -1394,7 +1399,11 @@
     public static final boolean IS_USER = "user".equals(TYPE);
 
     /**
-     * Whether this build is running inside a container.
+     * Whether this build is running on ARC, the Android Runtime for Chrome
+     * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md).
+     * Prior to R this was implemented as a container but from R this will be
+     * a VM. The name of the property remains ro.boot.conntainer as it is
+     * referenced in other projects.
      *
      * We should try to avoid checking this flag if possible to minimize
      * unnecessarily diverging from non-container Android behavior.
@@ -1405,7 +1414,7 @@
      * For higher-level behavior differences, other checks should be preferred.
      * @hide
      */
-    public static final boolean IS_CONTAINER =
+    public static final boolean IS_ARC =
             SystemProperties.getBoolean("ro.boot.container", false);
 
     /**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2ed0bad..308e6d5 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -189,13 +189,11 @@
         }
 
         @UnsupportedAppUsage
-        @Deprecated
         public File getExternalStorageDirectory() {
             return getExternalDirs()[0];
         }
 
         @UnsupportedAppUsage
-        @Deprecated
         public File getExternalStoragePublicDirectory(String type) {
             return buildExternalStoragePublicDirs(type)[0];
         }
@@ -698,11 +696,7 @@
      *
      * @see #getExternalStorageState()
      * @see #isExternalStorageRemovable()
-     * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
-     *             {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
-     *             performance.
      */
-    @Deprecated
     public static File getExternalStorageDirectory() {
         throwIfUserRequired();
         return sCurrentUser.getExternalDirs()[0];
@@ -1009,11 +1003,7 @@
      * @return Returns the File path for the directory. Note that this directory
      *         may not yet exist, so you must make sure it exists before using
      *         it such as with {@link File#mkdirs File.mkdirs()}.
-     * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
-     *             {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
-     *             performance.
      */
-    @Deprecated
     public static File getExternalStoragePublicDirectory(String type) {
         throwIfUserRequired();
         return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index be21fea..b3416e9 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.app.Activity;
+import android.app.GameManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -26,8 +27,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -37,9 +36,6 @@
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
@@ -88,9 +84,6 @@
     private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
     private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
 
-    // ANGLE related properties.
-    private static final String ANGLE_RULES_FILE = "a4a_rules.json";
-    private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
     private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
             "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
@@ -121,6 +114,7 @@
     private ClassLoader mClassLoader;
     private String mLibrarySearchPaths;
     private String mLibraryPermittedPaths;
+    private GameManager mGameManager;
 
     private int mAngleOptInIndex = -1;
 
@@ -133,6 +127,8 @@
         final ApplicationInfo appInfoWithMetaData =
                 getAppInfoWithMetadata(context, pm, packageName);
 
+        mGameManager = context.getSystemService(GameManager.class);
+
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
         setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -151,6 +147,23 @@
     }
 
     /**
+     * Query to determine if the Game Mode has enabled ANGLE.
+     */
+    private boolean isAngleEnabledByGameMode(Context context, String packageName) {
+        try {
+            final boolean gameModeEnabledAngle =
+                    (mGameManager != null) && mGameManager.getAngleEnabled(packageName);
+            Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
+            return gameModeEnabledAngle;
+        } catch (SecurityException e) {
+            Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
+                    + "for package: " + packageName);
+        }
+
+        return false;
+    }
+
+    /**
      * Query to determine if ANGLE should be used
      */
     private boolean shouldUseAngle(Context context, Bundle coreSettings,
@@ -164,21 +177,16 @@
         Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
                 + "set to: '" + devOptIn + "'");
 
-        // We only want to use ANGLE if the app is in the allowlist, or the developer has
-        // explicitly chosen something other than default driver.
-        // The allowlist will be generated by the ANGLE APK at both boot time and
-        // ANGLE update time. It will only include apps mentioned in the rules file.
-        final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+        // We only want to use ANGLE if the developer has explicitly chosen something other than
+        // default driver.
         final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
-
-        if (allowed) {
-            Log.v(TAG, "ANGLE allowlist includes " + packageName);
-        }
         if (requested) {
             Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
         }
 
-        return allowed || requested;
+        final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+
+        return requested || gameModeEnabledAngle;
     }
 
     private int getVulkanVersion(PackageManager pm) {
@@ -475,117 +483,6 @@
     }
 
     /**
-     * Attempt to setup ANGLE with a temporary rules file.
-     * True: Temporary rules file was loaded.
-     * False: Temporary rules file was *not* loaded.
-     */
-    private boolean setupAngleWithTempRulesFile(Context context,
-                                                String packageName,
-                                                String paths,
-                                                String devOptIn) {
-        /**
-         * We only want to load a temp rules file for:
-         *  - apps that are marked 'debuggable' in their manifest
-         *  - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
-         *    debugging (PR_SET_DUMPABLE).
-         */
-        if (!isDebuggable()) {
-            Log.v(TAG, "Skipping loading temporary rules file");
-            return false;
-        }
-
-        final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-
-        if (TextUtils.isEmpty(angleTempRules)) {
-            Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
-            return false;
-        }
-
-        Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-
-        final File tempRulesFile = new File(angleTempRules);
-        if (tempRulesFile.exists()) {
-            Log.i(TAG, angleTempRules + " exists, loading file.");
-            try {
-                final FileInputStream stream = new FileInputStream(angleTempRules);
-
-                try {
-                    final FileDescriptor rulesFd = stream.getFD();
-                    final long rulesOffset = 0;
-                    final long rulesLength = stream.getChannel().size();
-                    Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-
-                    setAngleInfo(paths, packageName, devOptIn, null,
-                            rulesFd, rulesOffset, rulesLength);
-
-                    stream.close();
-
-                    // We successfully setup ANGLE, so return with good status
-                    return true;
-                } catch (IOException e) {
-                    Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
-                }
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Temp ANGLE rules file not found: " + e);
-            } catch (SecurityException e) {
-                Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
-     * True: APK rules file was loaded.
-     * False: APK rules file was *not* loaded.
-     */
-    private boolean setupAngleRulesApk(String anglePkgName,
-            ApplicationInfo angleInfo,
-            PackageManager pm,
-            String packageName,
-            String paths,
-            String devOptIn,
-            String[] features) {
-        // Pass the rules file to loader for ANGLE decisions
-        try {
-            final AssetManager angleAssets = pm.getResourcesForApplication(angleInfo).getAssets();
-
-            try {
-                final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-
-                setAngleInfo(paths, packageName, devOptIn, features, assetsFd.getFileDescriptor(),
-                        assetsFd.getStartOffset(), assetsFd.getLength());
-
-                assetsFd.close();
-
-                return true;
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
-                        + " from '" + anglePkgName + "': " + e);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
-        }
-
-        return false;
-    }
-
-    /**
-     * Pull ANGLE allowlist from GlobalSettings and compare against current package
-     */
-    private boolean checkAngleAllowlist(Context context, Bundle bundle, String packageName) {
-        final ContentResolver contentResolver = context.getContentResolver();
-        final List<String> angleAllowlist =
-                getGlobalSettingsString(contentResolver, bundle,
-                    Settings.Global.ANGLE_ALLOWLIST);
-
-        if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
-
-        return angleAllowlist.contains(packageName);
-    }
-
-    /**
      * Pass ANGLE details down to trigger enable logic
      *
      * @param context
@@ -647,28 +544,21 @@
 
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
-        // If the user has set the developer option to something other than default,
-        // we need to call setupAngleRulesApk() with the package name and the developer
-        // option value (native/angle/other). Then later when we are actually trying to
-        // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
-        // and can confidently answer yes/no based on the previously set developer
-        // option value.
-        final String devOptIn = getDriverForPackage(context, bundle, packageName);
-
-        if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
-            // We setup ANGLE with a temp rules file, so we're done here.
-            return true;
+        // We need to call setAngleInfo() with the package name and the developer option value
+        //(native/angle/other). Then later when we are actually trying to load a driver,
+        //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently
+        //answer yes/no based on the previously set developer option value.
+        final String devOptIn;
+        final String[] features = getAngleEglFeatures(context, bundle);
+        final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+        if (gameModeEnabledAngle) {
+            devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE;
+        } else {
+            devOptIn = getDriverForPackage(context, bundle, packageName);
         }
+        setAngleInfo(paths, packageName, devOptIn, features);
 
-        String[] features = getAngleEglFeatures(context, bundle);
-
-        if (setupAngleRulesApk(
-                anglePkgName, angleInfo, pm, packageName, paths, devOptIn, features)) {
-            // ANGLE with rules is set up from the APK, hence return.
-            return true;
-        }
-
-        return false;
+        return true;
     }
 
     /**
@@ -956,7 +846,7 @@
     private static native void setGpuStats(String driverPackageName, String driverVersionName,
             long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
-            String[] features, FileDescriptor rulesFd, long rulesOffset, long rulesLength);
+            String[] features);
     private static native boolean getShouldUseAngle(String packageName);
     private static native boolean setInjectLayersPrSetDumpable();
 
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index e5e9b5f..cad6e66 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -316,7 +316,7 @@
                         new MyReadMapCallback()));
             }
         }
-        return EMPTY;
+        return new PersistableBundle();  // An empty mutable PersistableBundle
     }
 
     @Override
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 136e3de..f9b1695 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -93,6 +94,18 @@
         }
     }
 
+    /**
+     * Creates from existing shared memory passed as {@link ParcelFileDesciptor}.
+     *
+     * @param fd File descriptor of shared memory passed as {@link #ParcelFileDescriptor}.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static @NonNull SharedMemory create(@NonNull ParcelFileDescriptor fd) {
+        return new SharedMemory(fd.getFileDescriptor());
+    }
+
     private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
             | OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
 
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 44c3d61..7455f1f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -115,20 +115,18 @@
  *
  * <pre>
  * public void onCreate() {
- *     if (DEVELOPER_MODE) {
- *         StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- *                 .detectDiskReads()
- *                 .detectDiskWrites()
- *                 .detectNetwork()   // or .detectAll() for all detectable problems
- *                 .penaltyLog()
- *                 .build());
- *         StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- *                 .detectLeakedSqlLiteObjects()
- *                 .detectLeakedClosableObjects()
- *                 .penaltyLog()
- *                 .penaltyDeath()
- *                 .build());
- *     }
+ *     StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ *             .detectDiskReads()
+ *             .detectDiskWrites()
+ *             .detectNetwork()   // or .detectAll() for all detectable problems
+ *             .penaltyLog()
+ *             .build());
+ *     StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ *             .detectLeakedSqlLiteObjects()
+ *             .detectLeakedClosableObjects()
+ *             .penaltyLog()
+ *             .penaltyDeath()
+ *             .build());
  *     super.onCreate();
  * }
  * </pre>
@@ -147,9 +145,7 @@
  * <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
  * network accesses. While it does propagate its state across process boundaries when doing {@link
  * android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
- * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or
- * fewer) operations, so you should never leave StrictMode enabled in applications distributed on
- * Google Play.
+ * access from JNI calls won't necessarily trigger it.
  */
 public final class StrictMode {
     private static final String TAG = "StrictMode";
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index c8cbc51..ddb6533 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -102,8 +102,6 @@
     /** @hide */
     public static final long TRACE_TAG_RRO = 1L << 26;
     /** @hide */
-    public static final long TRACE_TAG_SYSPROP = 1L << 27;
-    /** @hide */
     public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8709f07..09b7aaa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2491,6 +2491,10 @@
                         throw re.rethrowFromSystemServer();
                     }
                 }
+                @Override
+                protected boolean bypass(Integer query) {
+                    return query < 0;
+                }
             };
 
     // Uses IS_USER_UNLOCKED_PROPERTY for invalidation as the APIs have the same dependencies.
@@ -2505,6 +2509,10 @@
                         throw re.rethrowFromSystemServer();
                     }
                 }
+                @Override
+                protected boolean bypass(Integer query) {
+                    return query < 0;
+                }
             };
 
     /** {@hide} */
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d7893e4..feffcbd 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -352,6 +352,8 @@
     /**
      * Vibrate constantly for the specified period of time.
      *
+     * <p>The app should be in foreground for the vibration to happen.</p>
+     *
      * @param milliseconds The number of milliseconds to vibrate.
      * @deprecated Use {@link #vibrate(VibrationEffect)} instead.
      */
@@ -364,6 +366,9 @@
     /**
      * Vibrate constantly for the specified period of time.
      *
+     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
      * @param milliseconds The number of milliseconds to vibrate.
      * @param attributes   {@link AudioAttributes} corresponding to the vibration. For example,
      *                     specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -398,6 +403,8 @@
      * to start the repeat, or -1 to disable repeating.
      * </p>
      *
+     * <p>The app should be in foreground for the vibration to happen.</p>
+     *
      * @param pattern an array of longs of times for which to turn the vibrator on or off.
      * @param repeat  the index into pattern at which to repeat, or -1 if
      *                you don't want to repeat.
@@ -423,6 +430,9 @@
      * to start the repeat, or -1 to disable repeating.
      * </p>
      *
+     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
      * @param pattern    an array of longs of times for which to turn the vibrator on or off.
      * @param repeat     the index into pattern at which to repeat, or -1 if
      *                   you don't want to repeat.
@@ -450,11 +460,30 @@
         }
     }
 
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in foreground for the vibration to happen.</p>
+     *
+     * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+     */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
     public void vibrate(VibrationEffect vibe) {
         vibrate(vibe, null);
     }
 
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
+     * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+     *                   specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+     *                   vibrations associated with incoming calls.
+     */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
     public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
         vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 9385402..fbac954 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -198,9 +198,9 @@
     void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
     void fixupAppDir(in String path) = 89;
     void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90;
-    void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91;
-    void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92;
-    PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 93;
-    boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 94;
-    int getExternalStorageMountMode(int uid, in String packageName) = 95;
-}
+    PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 91;
+    void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 92;
+    void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
+    int getExternalStorageMountMode(int uid, in String packageName) = 94;
+    boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+}
\ No newline at end of file
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 400b312..69a09fb 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -214,7 +214,7 @@
 
         return AudioManager.getAudioProductStrategies().stream()
                 .map(strategy -> strategy.getVolumeGroupIdForAudioAttributes(
-                        AudioProductStrategy.sDefaultAttributes))
+                        AudioProductStrategy.getDefaultAttributes()))
                 .filter(volumeGroupId -> volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP)
                 .findFirst()
                 .orElse(AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 376d942..b3d60a5 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8594,6 +8594,12 @@
      * <p>
      * Contacts-specific settings for various {@link Account}'s.
      * </p>
+     * <p>
+     * A settings entry for an account is created automatically when a raw contact or group
+     * is inserted that references it. Settings entries cannot be deleted as long as raw
+     * contacts or groups continue to reference it; in order to delete a settings entry all
+     * raw contacts and groups referencing the account must be deleted first.
+     * </p>
      * <h2>Columns</h2>
      * <table class="jd-sumtable">
      * <tr>
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 431bf4c..ded598a 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -432,6 +432,25 @@
     public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
 
     /**
+     * Namespace for all SurfaceFlinger features that are used at the native level.
+     * These features are applied on boot or after reboot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT =
+            "surface_flinger_native_boot";
+
+    /**
+     * Namespace for swcodec native related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
+
+
+    /**
      * Namespace for System UI related features.
      *
      * @hide
@@ -597,6 +616,14 @@
     @TestApi
     public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
 
+    /**
+     * Namespace for App Compat Overrides related features.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
@@ -767,7 +794,7 @@
     }
 
     /**
-     * Create a new property with the the provided name and value in the provided namespace, or
+     * Create a new property with the provided name and value in the provided namespace, or
      * update the value of such a property if it already exists. The same name can exist in multiple
      * namespaces and might have different values in any or all namespaces.
      * <p>
@@ -817,6 +844,22 @@
     }
 
     /**
+     * Delete a property with the provided name and value in the provided namespace
+     *
+     * @param namespace   The namespace containing the property to delete.
+     * @param name        The name of the property to delete.
+     * @return True if the property was deleted or it did not exist in the first place.
+     * False if the storage implementation throws errors.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(WRITE_DEVICE_CONFIG)
+    public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+        return Settings.Config.deleteString(contentResolver, namespace, name);
+    }
+
+    /**
      * Reset properties to their default values by removing the underlying values.
      * <p>
      * The method accepts an optional namespace parameter. If provided, only properties set within
@@ -851,32 +894,31 @@
     /**
      * Disables or re-enables bulk modifications ({@link #setProperties(Properties)}) to device
      * config values. This is intended for use during tests to prevent a sync operation clearing
-     * config values, which could influence the outcome of the tests, i.e. by changing behavior.
+     * config values which could influence the outcome of the tests, i.e. by changing behavior.
      *
      * @param syncDisabledMode the mode to use, see {@link Settings.Config#SYNC_DISABLED_MODE_NONE},
      *     {@link Settings.Config#SYNC_DISABLED_MODE_PERSISTENT} and {@link
      *     Settings.Config#SYNC_DISABLED_MODE_UNTIL_REBOOT}
      *
-     * @see #isSyncDisabled()
+     * @see #getSyncDisabledMode()
      * @hide
      */
     @RequiresPermission(WRITE_DEVICE_CONFIG)
-    public static void setSyncDisabled(@SyncDisabledMode int syncDisabledMode) {
+    public static void setSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        Settings.Config.setSyncDisabled(contentResolver, syncDisabledMode);
+        Settings.Config.setSyncDisabledMode(contentResolver, syncDisabledMode);
     }
 
     /**
-     * Returns the current state of sync disabling, {@code true} when disabled, {@code false}
-     * otherwise.
+     * Returns the current mode of sync disabling.
      *
-     * @see #setSyncDisabled(int)
+     * @see #setSyncDisabledMode(int)
      * @hide
      */
     @RequiresPermission(WRITE_DEVICE_CONFIG)
-    public static boolean isSyncDisabled() {
+    public static @SyncDisabledMode int getSyncDisabledMode() {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        return Settings.Config.isSyncDisabled(contentResolver);
+        return Settings.Config.getSyncDisabledMode(contentResolver);
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac520e8..d6d3412 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -299,8 +299,8 @@
     public static final String KEY_CONFIG_SET_ALL_RETURN = "config_set_all_return";
 
     /** @hide */
-    public static final String KEY_CONFIG_IS_SYNC_DISABLED_RETURN =
-            "config_is_sync_disabled_return";
+    public static final String KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN =
+            "config_get_sync_disabled_mode_return";
 
     /**
      * An int extra specifying a subscription ID.
@@ -2439,13 +2439,15 @@
     public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
 
     /** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
-    public static final String CALL_METHOD_SET_SYNC_DISABLED_CONFIG = "SET_SYNC_DISABLED_config";
+    public static final String CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG =
+            "SET_SYNC_DISABLED_MODE_config";
 
     /**
-     * @hide - Private call() method to return whether syncs are disabled for the 'configuration'
-     * table
+     * @hide - Private call() method to return the current mode of sync disabling for the
+     * 'configuration' table
      */
-    public static final String CALL_METHOD_IS_SYNC_DISABLED_CONFIG = "IS_SYNC_DISABLED_config";
+    public static final String CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG =
+            "GET_SYNC_DISABLED_MODE_config";
 
     /** @hide - Private call() method to register monitor callback for 'configuration' table */
     public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
@@ -2768,6 +2770,7 @@
         // for the fast path of retrieving settings.
         private final String mCallGetCommand;
         private final String mCallSetCommand;
+        private final String mCallDeleteCommand;
         private final String mCallListCommand;
         private final String mCallSetAllCommand;
 
@@ -2779,17 +2782,19 @@
         private GenerationTracker mGenerationTracker;
 
         <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
-                String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
-            this(uri, getCommand, setCommand, null, null, providerHolder,
+                String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+                Class<T> callerClass) {
+            this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
                     callerClass);
         }
 
         private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
-                String setCommand, String listCommand, String setAllCommand,
+                String setCommand, String deleteCommand, String listCommand, String setAllCommand,
                 ContentProviderHolder providerHolder, Class<T> callerClass) {
             mUri = uri;
             mCallGetCommand = getCommand;
             mCallSetCommand = setCommand;
+            mCallDeleteCommand = deleteCommand;
             mCallListCommand = listCommand;
             mCallSetAllCommand = setAllCommand;
             mProviderHolder = providerHolder;
@@ -2847,6 +2852,20 @@
             }
         }
 
+        public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+            try {
+                Bundle arg = new Bundle();
+                arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+                IContentProvider cp = mProviderHolder.getProvider(cr);
+                cp.call(cr.getAttributionSource(),
+                        mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+                return false;
+            }
+            return true;
+        }
+
         @UnsupportedAppUsage
         public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
             // Check if the target settings key is readable. Reject if the caller is not system and
@@ -3309,6 +3328,7 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SYSTEM,
                 CALL_METHOD_PUT_SYSTEM,
+                CALL_METHOD_DELETE_SYSTEM,
                 sProviderHolder,
                 System.class);
 
@@ -4698,7 +4718,7 @@
          *
          * @hide
          */
-        @Readable
+        @Readable(maxTargetSdk = Build.VERSION_CODES.R)
         public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
 
         /**
@@ -5629,6 +5649,7 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SECURE,
                 CALL_METHOD_PUT_SECURE,
+                CALL_METHOD_DELETE_SECURE,
                 sProviderHolder,
                 Secure.class);
 
@@ -8823,7 +8844,7 @@
 
         /**
          * Control if rotation suggestions are sent to System UI when in rotation locked mode.
-         * Done to enable screen rotation while the the screen rotation is locked. Enabling will
+         * Done to enable screen rotation while the screen rotation is locked. Enabling will
          * poll the accelerometer in rotation locked mode.
          *
          * If 0, then rotation suggestions are not sent to System UI. If 1, suggestions are sent.
@@ -10978,6 +10999,9 @@
          * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device
          * sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
          * system via {@code <System Audio Mode Request>}.</li>
+         * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM} Upon going to
+         * sleep, sends {@code <Standby>} to TV and Audio system. Upon waking up, device attempts
+         * to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
          * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep,
          * device sends {@code <Standby>} to all devices in the network. Upon waking up, device
          * attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
@@ -13670,13 +13694,6 @@
                 "angle_gl_driver_selection_values";
 
         /**
-         * List of package names that should check ANGLE rules
-         * @hide
-         */
-        @Readable
-        public static final String ANGLE_ALLOWLIST = "angle_allowlist";
-
-        /**
          * Lists of ANGLE EGL features for debugging.
          * Each list of features is separated by a comma, each feature in each list is separated by
          * a colon.
@@ -14324,8 +14341,11 @@
                 "are_user_disabled_hdr_formats_allowed";
 
         /**
-         * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are disabled
-         * currently. The value is boolean (1 or 0). The value '1' means that {@link
+         * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are currently
+         * persistently disabled. This is only used for the {@link
+         * Config#SYNC_DISABLED_MODE_PERSISTENT persistent} mode, {@link
+         * Config#SYNC_DISABLED_MODE_UNTIL_REBOOT until_reboot} mode is not stored in settings.
+         * The value is boolean (1 or 0). The value '1' means that {@link
          * DeviceConfig#setProperties(DeviceConfig.Properties)} will return {@code false}.
          *
          * @hide
@@ -14898,6 +14918,33 @@
                 "max_sound_trigger_detection_service_ops_per_day";
 
         /**
+         * Setting indicating the name of the Wear OS app package containing the device's sysui.
+         *
+         * @hide
+         */
+        public static final String CLOCKWORK_SYSUI_PACKAGE_NAME =
+                "clockwork_sysui_package_name";
+
+        /**
+         * Setting indicating the name of the main activity of the Wear OS sysui.
+         *
+         * @hide
+         */
+        public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME =
+                "clockwork_sysui_main_activity_name";
+
+        /**
+         * Setting to determine if the Clockwork Home application is ready.
+         *
+         * <p>
+         * Set to 1 when the Clockwork Home application has finished starting up.
+         * </p>
+         *
+         * @hide
+         */
+        public static final String CLOCKWORK_HOME_READY = "clockwork_home_ready";
+
+        /**
          * Indicates whether aware is available in the current location.
          * @hide
          */
@@ -14956,6 +15003,15 @@
         public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
 
         /**
+         * Global settings that shouldn't be persisted.
+         *
+         * @hide
+         */
+        public static final String[] TRANSIENT_SETTINGS = {
+                CLOCKWORK_HOME_READY,
+        };
+
+        /**
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
          *
@@ -14977,6 +15033,7 @@
                     CONTENT_URI,
                     CALL_METHOD_GET_GLOBAL,
                     CALL_METHOD_PUT_GLOBAL,
+                    CALL_METHOD_DELETE_GLOBAL,
                     sProviderHolder,
                     Global.class);
 
@@ -16158,6 +16215,464 @@
          * @hide
          */
         public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
+
+        /**
+         * Settings migrated from Wear OS settings provider.
+         * @hide
+         */
+        public static class Wearable {
+            /**
+             * Whether the user has any pay tokens on their watch.
+             * @hide
+             */
+            public static final String HAS_PAY_TOKENS = "has_pay_tokens";
+
+            /**
+             * Gcm checkin timeout in minutes.
+             * @hide
+             */
+            public static final String GMS_CHECKIN_TIMEOUT_MIN = "gms_checkin_timeout_min";
+
+            /**
+             * If hotword detection should be enabled.
+             * @hide
+             */
+            public static final String HOTWORD_DETECTION_ENABLED = "hotword_detection_enabled";
+
+            /**
+             * Whether Smart Replies are enabled within Wear.
+             * @hide
+             */
+            public static final String SMART_REPLIES_ENABLED = "smart_replies_enabled";
+
+            /**
+             * The default vibration pattern.
+             * @hide
+             */
+            public static final String DEFAULT_VIBRATION = "default_vibration";
+
+            /**
+             * If FLP should obtain location data from the paired device.
+             * @hide
+             */
+            public static final String OBTAIN_PAIRED_DEVICE_LOCATION =
+                    "obtain_paired_device_location";
+
+            /**
+             * Whether the device is in retail mode.
+             * @hide
+             */
+            public static final String RETAIL_MODE = "retail_mode";
+
+            // Possible retail mode states
+            /** @hide */
+            public static final int RETAIL_MODE_CONSUMER = 0;
+            /** @hide */
+            public static final int RETAIL_MODE_RETAIL = 1;
+
+            /**
+             * The play store availability.
+             * @hide
+             */
+            public static final String PLAY_STORE_AVAILABILITY = "play_store_availability";
+
+            // Possible play store availability states
+            /** @hide */
+            public static final int PLAY_STORE_AVAILABILITY_UNKNOWN = 0;
+            /** @hide */
+            public static final int PLAY_STORE_AVAILABLE = 1;
+            /** @hide */
+            public static final int PLAY_STORE_UNAVAILABLE = 2;
+
+            /**
+             * Whether the bug report is enabled.
+             * @hide
+             */
+            public static final String BUG_REPORT = "bug_report";
+
+            // Possible bug report states
+            /** @hide */
+            public static final int BUG_REPORT_DISABLED = 0;
+            /** @hide */
+            public static final int BUG_REPORT_ENABLED = 1;
+
+            /**
+             * The enabled/disabled state of the SmartIlluminate.
+             * @hide
+             */
+            public static final String SMART_ILLUMINATE_ENABLED = "smart_illuminate_enabled";
+
+            /**
+             * Whether automatic time is enabled on the watch.
+             * @hide
+             */
+            public static final String CLOCKWORK_AUTO_TIME = "clockwork_auto_time";
+
+            // Possible clockwork auto time states
+            /** @hide */
+            public static final int SYNC_TIME_FROM_PHONE = 0;
+            /** @hide */
+            public static final int SYNC_TIME_FROM_NETWORK = 1;
+            /** @hide */
+            public static final int AUTO_TIME_OFF = 2;
+            /** @hide */
+            public static final int INVALID_AUTO_TIME_STATE = 3;
+
+
+            /**
+             * Whether automatic time zone is enabled on the watch.
+             * @hide
+             */
+            public static final String CLOCKWORK_AUTO_TIME_ZONE = "clockwork_auto_time_zone";
+
+            // Possible clockwork auto time zone states
+            /** @hide */
+            public static final int SYNC_TIME_ZONE_FROM_PHONE = 0;
+            /** @hide */
+            public static final int SYNC_TIME_ZONE_FROM_NETWORK = 1;
+            /** @hide */
+            public static final int AUTO_TIME_ZONE_OFF = 2;
+            /** @hide */
+            public static final int INVALID_AUTO_TIME_ZONE_STATE = 3;
+
+            /**
+             * Whether 24 hour time format is enabled on the watch.
+             * @hide
+             */
+            public static final String CLOCKWORK_24HR_TIME = "clockwork_24hr_time";
+
+            /**
+             * Whether the auto wifi toggle setting is enabled.
+             * @hide
+             */
+            public static final String AUTO_WIFI = "auto_wifi";
+
+            // Possible force wifi on states
+            /** @hide */
+            public static final int AUTO_WIFI_DISABLED = 0;
+            /** @hide */
+            public static final int AUTO_WIFI_ENABLED = 1;
+
+            /**
+             * The number of minutes after the WiFi enters power save mode.
+             * @hide
+             */
+            public static final String WIFI_POWER_SAVE = "wifi_power_save";
+
+            /**
+             * The time at which we should no longer skip the wifi requirement check (we skip the
+             * wifi requirement until this time). The time is in millis since epoch.
+             * @hide
+             */
+            public static final String ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS =
+                    "alt_bypass_wifi_requirement_time_millis";
+
+            /**
+             * Whether or not Up/Down Gestures are enabled.
+             * @hide
+             */
+            public static final String UPDOWN_GESTURES_ENABLED = "updown_gestures_enabled";
+
+            /**
+             * Whether the setup was skipped.
+             * @hide
+             */
+            public static final String SETUP_SKIPPED = "setup_skipped";
+
+            // Possible setup_skipped states
+            /** @hide */
+            public static final int SETUP_SKIPPED_UNKNOWN = 0;
+            /** @hide */
+            public static final int SETUP_SKIPPED_YES = 1;
+            /** @hide */
+            public static final int SETUP_SKIPPED_NO = 2;
+
+            /**
+             * The last requested call forwarding action.
+             * @hide
+             */
+            public static final String LAST_CALL_FORWARD_ACTION = "last_call_forward_action";
+
+            // Possible call forwarding actions
+            /** @hide */
+            public static final int CALL_FORWARD_ACTION_ON = 1;
+            /** @hide */
+            public static final int CALL_FORWARD_ACTION_OFF = 2;
+            /** @hide */
+            public static final int CALL_FORWARD_NO_LAST_ACTION = -1;
+
+            // Stem button settings.
+            /** @hide */
+            public static final String STEM_1_TYPE = "STEM_1_TYPE";
+            /** @hide */
+            public static final String STEM_1_DATA = "STEM_1_DATA";
+            /** @hide */
+            public static final String STEM_1_DEFAULT_DATA = "STEM_1_DEFAULT_DATA";
+            /** @hide */
+            public static final String STEM_2_TYPE = "STEM_2_TYPE";
+            /** @hide */
+            public static final String STEM_2_DATA = "STEM_2_DATA";
+            /** @hide */
+            public static final String STEM_2_DEFAULT_DATA = "STEM_2_DEFAULT_DATA";
+            /** @hide */
+            public static final String STEM_3_TYPE = "STEM_3_TYPE";
+            /** @hide */
+            public static final String STEM_3_DATA = "STEM_3_DATA";
+            /** @hide */
+            public static final String STEM_3_DEFAULT_DATA = "STEM_3_DEFAULT_DATA";
+
+            // Stem types
+            /** @hide */
+            public static final int STEM_TYPE_UNKNOWN = -1;
+            /** @hide */
+            public static final int STEM_TYPE_APP_LAUNCH = 0;
+            /** @hide */
+            public static final int STEM_TYPE_CONTACT_LAUNCH = 1;
+
+            /**
+             * If the device should be muted when off body.
+             * @hide
+             */
+            public static final String MUTE_WHEN_OFF_BODY_ENABLED = "obtain_mute_when_off_body";
+
+            /**
+             * Wear OS version string.
+             * @hide
+             */
+            public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string";
+
+            /**
+             * If an alternate launcher is enabled.
+             * @hide
+             */
+            public static final String ALTERNATE_LAUNCHER_ENABLED = "alternate_launcher_enabled";
+
+            /**
+             * How round the corners of square screens are.
+             * @hide
+             */
+            public static final String CORNER_ROUNDNESS = "corner_roundness";
+
+            /**
+             * Whether the physical button has been set.
+             * @hide
+             */
+            public static final String BUTTON_SET = "button_set";
+
+            /**
+             * Whether there is a side button.
+             * @hide
+             */
+            public static final String SIDE_BUTTON = "side_button";
+
+            /**
+             * The android wear system version.
+             * @hide
+             */
+            public static final String ANDROID_WEAR_VERSION = "android_wear_version";
+
+            /**
+             * The wear system capabiltiies.
+             * @hide
+             */
+            public static final String SYSTEM_CAPABILITIES = "system_capabilities";
+
+            /**
+             * The android wear system edition.
+             * @hide
+             */
+            public static final String SYSTEM_EDITION = "android_wear_system_edition";
+
+            /**
+             * The Wear platform MR number.
+             * @hide
+             */
+            public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
+
+            /**
+             * The bluetooth settings storing duplicate address of companion device.
+             * @hide
+             */
+            public static final String COMPANION_BT_ADDRESS_DUAL = "companion_bt_address_dual";
+
+            /**
+             * The offset of the visible screen from the display bottom (overscan bottom).
+             * @hide
+             */
+            public static final String BOTTOM_OFFSET = "bottom_offset";
+
+            /**
+             * The shape of the display.
+             * @hide
+             */
+            public static final String DISPLAY_SHAPE = "display_shape";
+
+            // Possible display shapes
+            /** @hide */
+            public static final int DISPLAY_SHAPE_SQUARE = 0;
+            /** @hide */
+            public static final int DISPLAY_SHAPE_ROUND = 1;
+
+            /**
+             * The different levels of screen brightness the user can select.
+             * @hide
+             */
+            public static final String SCREEN_BRIGHTNESS_LEVEL = "screen_brightness_level";
+
+            /**
+             * The mobile signal detector setting.
+             * @hide
+             */
+            public static final String MOBILE_SIGNAL_DETECTOR = "mobile_signal_detector";
+
+
+            /**
+             * Whether ambient is currently enabled.
+             * @hide
+             */
+            public static final String AMBIENT_ENABLED = "ambient_enabled";
+
+            /**
+             * Whether ambient tilt to wake is enabled.
+             * @hide
+             */
+            public static final String AMBIENT_TILT_TO_WAKE = "ambient_tilt_to_wake";
+
+            /**
+             * Whether ambient low bit mode is enabled by developer options.
+             * @hide
+             */
+            public static final String AMBIENT_LOW_BIT_ENABLED_DEV = "ambient_low_bit_enabled_dev";
+
+            /**
+             * Whether ambient touch to wake is enabled.
+             * @hide
+             */
+            public static final String AMBIENT_TOUCH_TO_WAKE = "ambient_touch_to_wake";
+
+            /**
+             * Whether ambient tilt to bright is enabled.
+             * @hide
+             */
+            public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright";
+
+            /**
+             * Whether the current watchface is decomposable.
+             * @hide
+             */
+            public static final String DECOMPOSABLE_WATCHFACE = "current_watchface_decomposable";
+
+            /**
+             * Whether to force ambient when docked.
+             * @hide
+             */
+            public static final String AMBIENT_FORCE_WHEN_DOCKED = "ambient_force_when_docked";
+
+            /**
+             * The id of the gesture sensor.
+             * @hide
+             */
+            public static final String AMBIENT_GESTURE_SENSOR_ID = "ambient_gesture_sensor_id";
+
+            /**
+             * Whether the ambient low bit mode is enabled.
+             * @hide
+             */
+            public static final String AMBIENT_LOW_BIT_ENABLED = "ambient_low_bit_enabled";
+
+            /**
+             * The timeout duration in minutes of ambient mode when plugged in.
+             * @hide
+             */
+            public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min";
+
+            /**
+             * The companion device's bluetooth address.
+             * @hide
+             */
+            public static final String COMPANION_ADDRESS = "companion_address";
+
+            /**
+             * What OS does paired device has.
+             * @hide
+             */
+            public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type";
+
+            // Possible values of PAIRED_DEVICE_OS_TYPE
+            /** @hide */
+            public static final int PAIRED_DEVICE_OS_TYPE_UNKNOWN = 0;
+            /** @hide */
+            public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1;
+            /** @hide */
+            public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2;
+
+            /**
+             * The bluetooth settings selected BLE role for the companion.
+             * @hide
+             */
+            public static final String COMPANION_BLE_ROLE = "companion_ble_role";
+
+            // Possible values of COMPANION_BLE_ROLE
+            /** @hide */
+            public static final int BLUETOOTH_ROLE_CENTRAL = 1;
+            /** @hide */
+            public static final int BLUETOOTH_ROLE_PERIPHERAL = 2;
+
+            /**
+             * The bluetooth settings stored companion device name.
+             * @hide
+             */
+            public static final String COMPANION_NAME = "companion_bt_name";
+
+            /**
+             * The user's last setting for hfp client.
+             * @hide
+             */
+            public static final String USER_HFP_CLIENT_SETTING = "user_hfp_client_setting";
+
+            // Possible hfp client user setting values
+            /** @hide */
+            public static final int HFP_CLIENT_UNSET = 0;
+            /** @hide */
+            public static final int HFP_CLIENT_ENABLED = 1;
+            /** @hide */
+            public static final int HFP_CLIENT_DISABLED = 2;
+
+            /**
+             * The current HFP client profile setting.
+             * @hide
+             */
+            public static final String HFP_CLIENT_PROFILE_ENABLED = "hfp_client_profile_enabled";
+
+            /**
+             * The companion phone's android version.
+             * @hide
+             */
+            public static final String COMPANION_OS_VERSION = "wear_companion_os_version";
+
+            // Companion os version constants
+            /** @hide */
+            public static final int COMPANION_OS_VERSION_UNDEFINED = -1;
+
+            /**
+             * A boolean value to indicate if we want to support all languages in LE edition on
+             * wear. 1 for supporting, 0 for not supporting.
+             * @hide
+             */
+            public static final String ENABLE_ALL_LANGUAGES = "enable_all_languages";
+
+            /**
+             * The Locale (as language tag) the user chose at startup.
+             * @hide
+             */
+            public static final String SETUP_LOCALE = "setup_locale";
+
+            /**
+             * The version of oem setup present.
+             * @hide
+             */
+            public static final String OEM_SETUP_VERSION = "oem_setup_version";
+        }
     }
 
     /**
@@ -16209,6 +16724,7 @@
                 DeviceConfig.CONTENT_URI,
                 CALL_METHOD_GET_CONFIG,
                 CALL_METHOD_PUT_CONFIG,
+                CALL_METHOD_DELETE_CONFIG,
                 CALL_METHOD_LIST_CONFIG,
                 CALL_METHOD_SET_ALL_CONFIG,
                 sProviderHolder,
@@ -16318,6 +16834,26 @@
         }
 
         /**
+         * Delete a name/value pair from the database for the specified namespace.
+         *
+         * @param resolver to access the database with.
+         * @param namespace to delete the name/value pair from.
+         * @param name to delete.
+         * @return true if the value was deleted, false on database errors. If the name/value pair
+         * did not exist, return True.
+         *
+         * @see #resetToDefaults(ContentResolver, int, String)
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+        static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+                @NonNull String name) {
+            return sNameValueCache.deleteStringForUser(resolver,
+                    createCompositeName(namespace, name), resolver.getUserId());
+        }
+
+        /**
          * Reset the values to their defaults.
          * <p>
          * The method accepts an optional prefix parameter. If provided, only pairs with a name that
@@ -16350,47 +16886,47 @@
         }
 
         /**
-         * Bridge method between {@link DeviceConfig#setSyncDisabled(int)} and the
+         * Bridge method between {@link DeviceConfig#setSyncDisabledMode(int)} and the
          * {@link com.android.providers.settings.SettingsProvider} implementation.
          *
          * @hide
          */
         @SuppressLint("AndroidFrameworkRequiresPermission")
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static void setSyncDisabled(
+        static void setSyncDisabledMode(
                 @NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) {
             try {
                 Bundle args = new Bundle();
                 args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getAttributionSource(),
-                        sProviderHolder.mUri.getAuthority(), CALL_METHOD_SET_SYNC_DISABLED_CONFIG,
-                        null, args);
+                cp.call(resolver.getAttributionSource(), sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG, null, args);
             } catch (RemoteException e) {
-                Log.w(TAG, "Can't set sync disabled " + DeviceConfig.CONTENT_URI, e);
+                Log.w(TAG, "Can't set sync disabled mode " + DeviceConfig.CONTENT_URI, e);
             }
         }
 
         /**
-         * Bridge method between {@link DeviceConfig#isSyncDisabled()} and the
+         * Bridge method between {@link DeviceConfig#getSyncDisabledMode()} and the
          * {@link com.android.providers.settings.SettingsProvider} implementation.
          *
          * @hide
          */
         @SuppressLint("AndroidFrameworkRequiresPermission")
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static boolean isSyncDisabled(@NonNull ContentResolver resolver) {
+        static int getSyncDisabledMode(@NonNull ContentResolver resolver) {
             try {
                 Bundle args = Bundle.EMPTY;
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
                 Bundle bundle = cp.call(resolver.getAttributionSource(),
-                        sProviderHolder.mUri.getAuthority(), CALL_METHOD_IS_SYNC_DISABLED_CONFIG,
+                        sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG,
                         null, args);
-                return bundle.getBoolean(KEY_CONFIG_IS_SYNC_DISABLED_RETURN);
+                return bundle.getInt(KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN);
             } catch (RemoteException e) {
-                Log.w(TAG, "Can't query sync disabled " + DeviceConfig.CONTENT_URI, e);
+                Log.w(TAG, "Can't query sync disabled mode " + DeviceConfig.CONTENT_URI, e);
             }
-            return false;
+            return -1;
         }
 
         /**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f3a8b5d..387fc39 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5343,6 +5343,13 @@
         public static final String COLUMN_RCS_CONFIG = "rcs_config";
 
         /**
+         * TelephonyProvider column name for device to device sharing status.
+         *
+         * @hide
+         */
+        public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
+
+        /**
          * TelephonyProvider column name for VoIMS provisioning. Default is 0.
          * <P>Type: INTEGER </P>
          *
@@ -5351,13 +5358,6 @@
         public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
 
         /**
-         * TelephonyProvider column name for device to device sharing status.
-         *
-         * @hide
-         */
-        public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
-
-        /**
          * TelephonyProvider column name for information selected contacts that allow device to
          * device sharing.
          *
@@ -5365,5 +5365,6 @@
          */
         public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
                 "d2d_sharing_contacts";
+
     }
 }
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index d15514d..8eeecc2 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -28,6 +28,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Defines actions to be applied to a {@link RemoteViews template presentation}.
@@ -94,7 +95,7 @@
          */
         public Builder updateTemplate(@NonNull RemoteViews updates) {
             throwIfDestroyed();
-            mUpdates = Preconditions.checkNotNull(updates);
+            mUpdates = Objects.requireNonNull(updates);
             return this;
         }
 
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index e3e8844..5697e24 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -32,6 +32,7 @@
 
 import java.util.LinkedHashMap;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -147,9 +148,9 @@
         public Builder addField(@NonNull AutofillId id, @NonNull Pattern regex,
                 @NonNull String subst) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(id);
-            Preconditions.checkNotNull(regex);
-            Preconditions.checkNotNull(subst);
+            Objects.requireNonNull(id);
+            Objects.requireNonNull(regex);
+            Objects.requireNonNull(subst);
 
             mFields.put(id, new Pair<>(regex, subst));
             return this;
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
index c7dc15a..92952cb 100644
--- a/core/java/android/service/autofill/CompositeUserData.java
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -25,10 +25,9 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Objects;
 
 /**
  * Holds both a generic and package-specific userData used for
@@ -103,7 +102,7 @@
     @Nullable
     @Override
     public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
-        Preconditions.checkNotNull(categoryId);
+        Objects.requireNonNull(categoryId);
         final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
         if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
             return null;
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 6df0154..f3f912b 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,6 +32,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Defines a custom description for the autofill save UI.
@@ -157,7 +158,7 @@
          * {@link android.os.Build.VERSION_CODES#P} or higher).
          */
         public Builder(@NonNull RemoteViews parentPresentation) {
-            mPresentation = Preconditions.checkNotNull(parentPresentation);
+            mPresentation = Objects.requireNonNull(parentPresentation);
         }
 
         /**
@@ -276,7 +277,7 @@
             throwIfDestroyed();
             Preconditions.checkArgument((condition instanceof InternalValidator),
                     "not provided by Android System: %s", condition);
-            Preconditions.checkNotNull(updates);
+            Objects.requireNonNull(updates);
             if (mUpdates == null) {
                 mUpdates = new ArrayList<>();
             }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 87c0d9d..8539bf5 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -34,6 +34,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 /**
@@ -280,7 +281,7 @@
          * @param presentation The presentation used to visualize this dataset.
          */
         public Builder(@NonNull RemoteViews presentation) {
-            Preconditions.checkNotNull(presentation, "presentation must be non-null");
+            Objects.requireNonNull(presentation, "presentation must be non-null");
             mPresentation = presentation;
         }
 
@@ -296,7 +297,7 @@
          */
         @SystemApi
         public Builder(@NonNull InlinePresentation inlinePresentation) {
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
             mInlinePresentation = inlinePresentation;
         }
 
@@ -321,7 +322,7 @@
         public @NonNull Builder setInlinePresentation(
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
             mInlinePresentation = inlinePresentation;
             return this;
         }
@@ -343,8 +344,8 @@
                 @NonNull InlinePresentation inlinePresentation,
                 @NonNull InlinePresentation inlineTooltipPresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
-            Preconditions.checkNotNull(inlineTooltipPresentation,
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
+            Objects.requireNonNull(inlineTooltipPresentation,
                     "inlineTooltipPresentation must be non-null");
             mInlinePresentation = inlinePresentation;
             mInlineTooltipPresentation = inlineTooltipPresentation;
@@ -540,7 +541,7 @@
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(presentation, "presentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, null, null);
             return this;
         }
@@ -613,7 +614,7 @@
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @Nullable Pattern filter, @NonNull RemoteViews presentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(presentation, "presentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, null,
                     new DatasetFieldFilter(filter));
             return this;
@@ -644,8 +645,8 @@
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            Objects.requireNonNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
             return this;
         }
@@ -676,9 +677,9 @@
                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
                 @NonNull InlinePresentation inlineTooltipPresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
-            Preconditions.checkNotNull(inlineTooltipPresentation,
+            Objects.requireNonNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+            Objects.requireNonNull(inlineTooltipPresentation,
                     "inlineTooltipPresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
                     inlineTooltipPresentation, null);
@@ -722,8 +723,8 @@
                 @Nullable Pattern filter, @NonNull RemoteViews presentation,
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            Objects.requireNonNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
                     new DatasetFieldFilter(filter));
             return this;
@@ -761,9 +762,9 @@
                 @NonNull InlinePresentation inlinePresentation,
                 @NonNull InlinePresentation inlineTooltipPresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
-            Preconditions.checkNotNull(inlineTooltipPresentation,
+            Objects.requireNonNull(presentation, "presentation cannot be null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+            Objects.requireNonNull(inlineTooltipPresentation,
                     "inlineTooltipPresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
                     inlineTooltipPresentation, new DatasetFieldFilter(filter));
@@ -800,7 +801,7 @@
                 @Nullable AutofillValue value, @Nullable Pattern filter,
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
                     new DatasetFieldFilter(filter));
             return this;
@@ -819,7 +820,7 @@
                 @Nullable InlinePresentation inlinePresentation,
                 @Nullable InlinePresentation tooltip,
                 @Nullable DatasetFieldFilter filter) {
-            Preconditions.checkNotNull(id, "id cannot be null");
+            Objects.requireNonNull(id, "id cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
                 if (existingIdx >= 0) {
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 338ba74..7340857 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -29,9 +29,8 @@
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Date;
+import java.util.Objects;
 
 /**
  * Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of a field
@@ -57,8 +56,8 @@
      * @param dateFormat object used to transform the date value of the field to a String.
      */
     public DateTransformation(@NonNull AutofillId id, @NonNull DateFormat dateFormat) {
-        mFieldId = Preconditions.checkNotNull(id);
-        mDateFormat = Preconditions.checkNotNull(dateFormat);
+        mFieldId = Objects.requireNonNull(id);
+        mDateFormat = Objects.requireNonNull(dateFormat);
     }
 
     /** @hide */
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 707bab1..6f7808e 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -27,9 +27,8 @@
 import android.util.Log;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Date;
+import java.util.Objects;
 
 /**
  * Sanitizes a date {@link AutofillValue} using a {@link DateFormat}.
@@ -52,7 +51,7 @@
      * @param dateFormat date format applied to the actual date value of an input field.
       */
     public DateValueSanitizer(@NonNull DateFormat dateFormat) {
-        mDateFormat = Preconditions.checkNotNull(dateFormat);
+        mDateFormat = Objects.requireNonNull(dateFormat);
     }
 
     /** @hide */
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 5bf56cb9..5302b9b 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -22,12 +22,11 @@
 import android.os.Parcel;
 import android.view.autofill.Helper;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
@@ -39,7 +38,7 @@
 
     /** @hide */
     public FieldClassification(@NonNull ArrayList<Match> matches) {
-        mMatches = Preconditions.checkNotNull(matches);
+        mMatches = Objects.requireNonNull(matches);
         Collections.sort(mMatches, new Comparator<Match>() {
             @Override
             public int compare(Match o1, Match o2) {
@@ -113,7 +112,7 @@
 
         /** @hide */
         public Match(String categoryId, float score) {
-            mCategoryId = Preconditions.checkNotNull(categoryId);
+            mCategoryId = Objects.requireNonNull(categoryId);
             mScore = score;
         }
 
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 62becc5..af846b6 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -96,6 +96,8 @@
      */
     public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
 
+    // The flag value 0x20 has been defined in AutofillManager.
+
     /** @hide */
     public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 740ae26..970cb18 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,6 +41,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Response for an {@link
@@ -598,7 +599,7 @@
         public Builder setHeader(@NonNull RemoteViews header) {
             throwIfDestroyed();
             throwIfAuthenticationCalled();
-            mHeader = Preconditions.checkNotNull(header);
+            mHeader = Objects.requireNonNull(header);
             return this;
         }
 
@@ -630,7 +631,7 @@
         public Builder setFooter(@NonNull RemoteViews footer) {
             throwIfDestroyed();
             throwIfAuthenticationCalled();
-            mFooter = Preconditions.checkNotNull(footer);
+            mFooter = Objects.requireNonNull(footer);
             return this;
         }
 
@@ -649,7 +650,7 @@
         public Builder setUserData(@NonNull UserData userData) {
             throwIfDestroyed();
             throwIfAuthenticationCalled();
-            mUserData = Preconditions.checkNotNull(userData);
+            mUserData = Objects.requireNonNull(userData);
             return this;
         }
 
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 974f0ea..e317159 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -33,6 +33,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 /**
@@ -127,7 +128,7 @@
          */
         @Deprecated
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
-            mId = Preconditions.checkNotNull(id);
+            mId = Objects.requireNonNull(id);
             addOption(regex, resId);
         }
 
@@ -143,7 +144,7 @@
          */
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
                 @NonNull CharSequence contentDescription) {
-            mId = Preconditions.checkNotNull(id);
+            mId = Objects.requireNonNull(id);
             addOption(regex, resId, contentDescription);
         }
 
@@ -177,7 +178,7 @@
          */
         public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
                 @NonNull CharSequence contentDescription) {
-            addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+            addOptionInternal(regex, resId, Objects.requireNonNull(contentDescription));
             return this;
         }
 
@@ -185,7 +186,7 @@
                 @Nullable CharSequence contentDescription) {
             throwIfDestroyed();
 
-            Preconditions.checkNotNull(regex);
+            Objects.requireNonNull(regex);
             Preconditions.checkArgument(resId != 0);
 
             mOptions.add(new Option(regex, resId, contentDescription));
diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java
index 2f098e2..d626845 100644
--- a/core/java/android/service/autofill/NegationValidator.java
+++ b/core/java/android/service/autofill/NegationValidator.java
@@ -22,7 +22,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 /**
  * Validator used to implement a {@code NOT} logical operation.
@@ -33,7 +33,7 @@
     @NonNull private final InternalValidator mValidator;
 
     NegationValidator(@NonNull InternalValidator validator) {
-        mValidator = Preconditions.checkNotNull(validator);
+        mValidator =  Objects.requireNonNull(validator);
     }
 
     @Override
diff --git a/core/java/android/service/autofill/RegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
index 8cb67d0..00c4347 100644
--- a/core/java/android/service/autofill/RegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -25,8 +25,7 @@
 import android.util.Log;
 import android.view.autofill.AutofillId;
 
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 /**
@@ -50,8 +49,8 @@
      * otherwise, it returns {@code false}.
       */
     public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
-        mId = Preconditions.checkNotNull(id);
-        mRegex = Preconditions.checkNotNull(regex);
+        mId = Objects.requireNonNull(id);
+        mRegex = Objects.requireNonNull(regex);
     }
 
     /** @hide */
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 1753ecf..87a869f 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -23,7 +23,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 /**
  * Handles save requests from the {@link AutofillService} into the {@link Activity} being
@@ -70,7 +70,7 @@
      * or {@link #onFailure(CharSequence)} was already called.
      */
     public void onSuccess(@NonNull IntentSender intentSender) {
-        onSuccessInternal(Preconditions.checkNotNull(intentSender));
+        onSuccessInternal(Objects.requireNonNull(intentSender));
     }
 
     private void onSuccessInternal(@Nullable IntentSender intentSender) {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 619bfa2..8edfde8 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -39,6 +39,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Information used to indicate that an {@link AutofillService} is interested on saving the
@@ -769,7 +770,7 @@
          */
         public @NonNull Builder setTriggerId(@NonNull AutofillId id) {
             throwIfDestroyed();
-            mTriggerId = Preconditions.checkNotNull(id);
+            mTriggerId = Objects.requireNonNull(id);
             return this;
         }
 
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 5dd07c4..3ad5352 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -22,10 +22,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * This class represents a request to an {@link AutofillService
@@ -41,7 +40,7 @@
     /** @hide */
     public SaveRequest(@NonNull ArrayList<FillContext> fillContexts,
             @Nullable Bundle clientState, @Nullable ArrayList<String> datasetIds) {
-        mFillContexts = Preconditions.checkNotNull(fillContexts, "fillContexts");
+        mFillContexts = Objects.requireNonNull(fillContexts, "fillContexts");
         mClientState = clientState;
         mDatasetIds = datasetIds;
     }
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index cc48fcb..5bafa7a 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -26,8 +26,7 @@
 import android.util.Slog;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -57,8 +56,8 @@
      * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
      */
     public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
-        mRegex = Preconditions.checkNotNull(regex);
-        mSubst = Preconditions.checkNotNull(subst);
+        mRegex = Objects.requireNonNull(regex);
+        mSubst = Objects.requireNonNull(subst);
     }
 
     /** @hide */
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index eaffc92..e9d5203 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -43,6 +43,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Defines the user data used for
@@ -108,7 +109,7 @@
     @Nullable
     @Override
     public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
-        Preconditions.checkNotNull(categoryId);
+        Objects.requireNonNull(categoryId);
         if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
             return null;
         }
@@ -296,7 +297,7 @@
         public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
                 @Nullable String name, @Nullable Bundle args) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(categoryId);
+            Objects.requireNonNull(categoryId);
             if (mCategoryAlgorithms == null) {
                 mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
             }
@@ -368,13 +369,13 @@
         }
 
         private String checkNotEmpty(@NonNull String name, @Nullable String value) {
-            Preconditions.checkNotNull(value);
+            Objects.requireNonNull(value);
             Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
             return value;
         }
 
         private void checkValidValue(@Nullable String value) {
-            Preconditions.checkNotNull(value);
+            Objects.requireNonNull(value);
             final int length = value.length();
             Preconditions.checkArgumentInRange(length, getMinValueLength(),
                     getMaxValueLength(), "value length (" + length + ")");
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 7cd674e..fd0520d 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -26,9 +26,8 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Object used to interact with the autofill system.
@@ -52,7 +51,7 @@
      * automatically {@link FillWindow#destroy() destroyed}.
      */
     public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
-        Preconditions.checkNotNull(values);
+        Objects.requireNonNull(values);
 
         if (sDebug) {
             Log.d(TAG, "autofill() with " + values.size() + " values");
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index d4f7e11..0ce040d 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -36,12 +36,12 @@
 import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
 import dalvik.system.CloseGuard;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.util.Objects;
 
 /**
  * Handle to a window used to display the augmented autofill UI.
@@ -103,9 +103,9 @@
             Log.d(TAG, "Updating " + area + " + with " + rootView);
         }
         // TODO(b/123100712): add test case for null
-        Preconditions.checkNotNull(area);
-        Preconditions.checkNotNull(area.proxy);
-        Preconditions.checkNotNull(rootView);
+        Objects.requireNonNull(area);
+        Objects.requireNonNull(area.proxy);
+        Objects.requireNonNull(rootView);
         // TODO(b/123100712): must check the area is a valid object returned by
         // SmartSuggestionParams, throw IAE if not
 
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 3c44cfd..737c95f 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -54,7 +54,6 @@
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -62,6 +61,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -544,8 +544,8 @@
             @Override
             public void onAccept(@NonNull Executor executor,
                     @NonNull DataShareReadAdapter adapter) {
-                Preconditions.checkNotNull(adapter);
-                Preconditions.checkNotNull(executor);
+                Objects.requireNonNull(adapter);
+                Objects.requireNonNull(executor);
 
                 DataShareReadAdapterDelegate delegate =
                         new DataShareReadAdapterDelegate(executor, adapter,
@@ -661,9 +661,9 @@
 
         DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter,
                 LocalDataShareAdapterResourceManager resourceManager) {
-            Preconditions.checkNotNull(executor);
-            Preconditions.checkNotNull(adapter);
-            Preconditions.checkNotNull(resourceManager);
+            Objects.requireNonNull(executor);
+            Objects.requireNonNull(adapter);
+            Objects.requireNonNull(resourceManager);
 
             resourceManager.initializeForDelegate(this, adapter, executor);
             mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 4fd36e5..91042bfa 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -91,7 +91,8 @@
             = "android.service.notification.NotificationAssistantService";
 
     /**
-     * Data type: int, the feedback rating score provided by user
+     * Data type: int, the feedback rating score provided by user. The score can be any integer
+     *            value depends on the experimental and feedback UX design.
      */
     public static final String FEEDBACK_RATING = "feedback.rating";
 
@@ -129,7 +130,8 @@
      * A notification was posted by an app. Called before post.
      *
      * <p>Note: this method is only called if you don't override
-     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)} or
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
      *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 200ms.
@@ -139,6 +141,9 @@
     /**
      * A notification was posted by an app. Called before post.
      *
+     * <p>Note: this method is only called if you don't override
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
+     *
      * @param sbn the new notification
      * @param channel the channel the notification was posted to
      * @return an adjustment or null to take no action, within 200ms.
@@ -282,7 +287,7 @@
      * @param key the notification key
      * @param rankingMap The current ranking map that can be used to retrieve ranking information
      *                   for active notifications.
-     * @param feedback the feedback detail
+     * @param feedback the received feedback, such as {@link #FEEDBACK_RATING rating score}
      */
     public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
             @NonNull Bundle feedback) {
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 314c97d..1e5ff3a 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -20,6 +20,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Calendar;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -92,16 +94,22 @@
      */
     public long getNextChangeTime(long now) {
         if (mSchedule == null) return 0;
-        final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute);
-        final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute);
+        final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute, true);
+        final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute, false);
         long nextScheduleTime = Math.min(nextStart, nextEnd);
 
         return nextScheduleTime;
     }
 
-    private long getNextTime(long now, int hr, int min) {
-        final long time = getTime(now, hr, min);
-        return time <= now ? addDays(time, 1) : time;
+    private long getNextTime(long now, int hr, int min, boolean adjust) {
+        // The adjust parameter indicates whether to potentially adjust the time to the closest
+        // actual time if the indicated time is one skipped due to daylight time.
+        final long time = adjust ? getClosestActualTime(now, hr, min) : getTime(now, hr, min);
+        if (time <= now) {
+            final long tomorrow = addDays(time, 1);
+            return adjust ? getClosestActualTime(tomorrow, hr, min) : getTime(tomorrow, hr, min);
+        }
+        return time;
     }
 
     private long getTime(long millis, int hour, int min) {
@@ -119,7 +127,7 @@
      */
     public boolean isInSchedule(long time) {
         if (mSchedule == null || mDays.size() == 0) return false;
-        final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute);
+        final long start = getClosestActualTime(time, mSchedule.startHour, mSchedule.startMinute);
         long end = getTime(time, mSchedule.endHour, mSchedule.endMinute);
         if (end <= start) {
             end = addDays(end, 1);
@@ -134,7 +142,7 @@
      */
     public boolean isAlarmInSchedule(long alarm, long now) {
         if (mSchedule == null || mDays.size() == 0) return false;
-        final long start = getTime(alarm, mSchedule.startHour, mSchedule.startMinute);
+        final long start = getClosestActualTime(alarm, mSchedule.startHour, mSchedule.startMinute);
         long end = getTime(alarm, mSchedule.endHour, mSchedule.endMinute);
         if (end <= start) {
             end = addDays(end, 1);
@@ -186,4 +194,41 @@
         mCalendar.add(Calendar.DATE, days);
         return mCalendar.getTimeInMillis();
     }
+
+    /**
+     * This function returns the closest "actual" time to the provided hour/minute relative to the
+     * reference time. For most times this will behave exactly the same as getTime, but for any time
+     * during the hour skipped forward for daylight savings time (for instance, 02:xx when the
+     * clock is set to 03:00 after 01:59), this method will return the time when the clock changes
+     * (in this example, 03:00).
+     *
+     * Assumptions made in this implementation:
+     *   - Time is moved forward on an hour boundary (minute 0) by exactly 1hr when clocks shift
+     *   - a lenient Calendar implementation will interpret 02:xx on a day when 2-3AM is skipped
+     *     as 03:xx
+     *   - The skipped hour is never 11PM / 23:00.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public long getClosestActualTime(long refTime, int hour, int min) {
+        long resTime = getTime(refTime, hour, min);
+        if (!mCalendar.getTimeZone().observesDaylightTime()) {
+            // Do nothing if the timezone doesn't observe daylight time at all.
+            return resTime;
+        }
+
+        // Approach to identifying whether the time is "skipped": get the result from starting with
+        // refTime and setting hour and minute, then re-extract the hour and minute of the resulting
+        // moment in time. If the hour is exactly one more than the passed-in hour and the minute is
+        // the same, then the provided hour is likely a skipped one. If the time doesn't fall into
+        // this category, return the unmodified time instead.
+        mCalendar.setTimeInMillis(resTime);
+        int resHr = mCalendar.get(Calendar.HOUR_OF_DAY);
+        int resMin = mCalendar.get(Calendar.MINUTE);
+        if (resHr == hour + 1 && resMin == min) {
+            return getTime(refTime, resHr, 0);
+        }
+        return resTime;
+    }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ee8353a..45bdb11 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -19,9 +19,11 @@
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -79,7 +81,7 @@
     public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
     public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
     public static final int MAX_SOURCE = SOURCE_STAR;
-    private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
+    private static final int DEFAULT_SOURCE = SOURCE_STAR;
     private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
 
     public static final String MANUAL_RULE_ID = "MANUAL_RULE";
@@ -103,14 +105,17 @@
     private static final boolean DEFAULT_ALLOW_MEDIA = true;
     private static final boolean DEFAULT_ALLOW_SYSTEM = false;
     private static final boolean DEFAULT_ALLOW_CALLS = true;
-    private static final boolean DEFAULT_ALLOW_MESSAGES = false;
+    private static final boolean DEFAULT_ALLOW_MESSAGES = true;
     private static final boolean DEFAULT_ALLOW_REMINDERS = false;
     private static final boolean DEFAULT_ALLOW_EVENTS = false;
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
-    private static final boolean DEFAULT_ALLOW_CONV = false;
-    private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_NONE;
+    private static final boolean DEFAULT_ALLOW_CONV = true;
+    private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
     private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
-    private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
+    // Default setting here is 010011101 = 157
+    private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
+            SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+                    | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
 
     public static final int XML_VERSION = 8;
     public static final String ZEN_TAG = "zen";
@@ -568,16 +573,22 @@
 
                     // migrate old suppressed visual effects fields, if they still exist in the xml
                     Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
-                    if (allowWhenScreenOff != null) {
+                    Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
+                    if (allowWhenScreenOff != null || allowWhenScreenOn != null) {
+                        // If either setting exists, then reset the suppressed visual effects field
+                        // to 0 (all allowed) so that only the relevant bits are disallowed by
+                        // the migrated settings.
                         readSuppressedEffects = true;
+                        rt.suppressedVisualEffects = 0;
+                    }
+                    if (allowWhenScreenOff != null) {
                         if (!allowWhenScreenOff) {
                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
-                                    | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+                                    | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+                                    | SUPPRESSED_EFFECT_AMBIENT;
                         }
                     }
-                    Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
                     if (allowWhenScreenOn != null) {
-                        readSuppressedEffects = true;
                         if (!allowWhenScreenOn) {
                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
                         }
@@ -1001,7 +1012,7 @@
             builder.showBadges(
                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
             builder.showInAmbientDisplay(
-                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+                    (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0);
             builder.showInNotificationList(
                     (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
         }
@@ -1085,7 +1096,7 @@
 
         boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
                 ZenPolicy.VISUAL_EFFECT_AMBIENT,
-                isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+                isVisualEffectAllowed(SUPPRESSED_EFFECT_AMBIENT,
                         defaultPolicy));
 
         if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
@@ -1120,7 +1131,7 @@
         }
 
         if (suppressAmbient) {
-            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+            suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
         }
 
         if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 1fb18fa..29d430d 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -49,10 +49,9 @@
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -127,8 +126,8 @@
         public void onSuggestSelection(
                 TextClassificationSessionId sessionId,
                 TextSelection.Request request, ITextClassifierCallback callback) {
-            Preconditions.checkNotNull(request);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(callback);
             mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
                     sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
 
@@ -138,8 +137,8 @@
         public void onClassifyText(
                 TextClassificationSessionId sessionId,
                 TextClassification.Request request, ITextClassifierCallback callback) {
-            Preconditions.checkNotNull(request);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(callback);
             mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
                     sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
@@ -148,8 +147,8 @@
         public void onGenerateLinks(
                 TextClassificationSessionId sessionId,
                 TextLinks.Request request, ITextClassifierCallback callback) {
-            Preconditions.checkNotNull(request);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(callback);
             mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
                     sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
@@ -158,7 +157,7 @@
         public void onSelectionEvent(
                 TextClassificationSessionId sessionId,
                 SelectionEvent event) {
-            Preconditions.checkNotNull(event);
+            Objects.requireNonNull(event);
             mMainThreadHandler.post(
                     () -> TextClassifierService.this.onSelectionEvent(sessionId, event));
         }
@@ -167,7 +166,7 @@
         public void onTextClassifierEvent(
                 TextClassificationSessionId sessionId,
                 TextClassifierEvent event) {
-            Preconditions.checkNotNull(event);
+            Objects.requireNonNull(event);
             mMainThreadHandler.post(
                     () -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
         }
@@ -177,8 +176,8 @@
                 TextClassificationSessionId sessionId,
                 TextLanguage.Request request,
                 ITextClassifierCallback callback) {
-            Preconditions.checkNotNull(request);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(callback);
             mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
                     sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
@@ -188,8 +187,8 @@
                 TextClassificationSessionId sessionId,
                 ConversationActions.Request request,
                 ITextClassifierCallback callback) {
-            Preconditions.checkNotNull(request);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(callback);
             mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
                     sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
@@ -197,8 +196,8 @@
         @Override
         public void onCreateTextClassificationSession(
                 TextClassificationContext context, TextClassificationSessionId sessionId) {
-            Preconditions.checkNotNull(context);
-            Preconditions.checkNotNull(sessionId);
+            Objects.requireNonNull(context);
+            Objects.requireNonNull(sessionId);
             mMainThreadHandler.post(
                     () -> TextClassifierService.this.onCreateTextClassificationSession(
                             context, sessionId));
@@ -487,7 +486,7 @@
         private ITextClassifierCallback mTextClassifierCallback;
 
         private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
-            mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
+            mTextClassifierCallback = Objects.requireNonNull(textClassifierCallback);
         }
 
         @Override
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index a2b22e8..b516b02 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -114,9 +114,15 @@
  *
  * <p>Threading:
  *
- * <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously
- * to the system server. Calls to {@link #onStartUpdates(long)} and {@link #onStopUpdates()} will
- * occur on a single thread.
+ * <p>Outgoing calls to {@code report} methods can be made on any thread and will be delivered
+ * asynchronously to the system server. Incoming calls to {@link TimeZoneProviderService}-defined
+ * service methods like {@link #onStartUpdates(long)} and {@link #onStopUpdates()} are also
+ * asynchronous with respect to the system server caller and will be delivered to this service using
+ * a single thread. {@link Service} lifecycle method calls like {@link #onCreate()} and {@link
+ * #onDestroy()} can occur on a different thread from those made to {@link
+ * TimeZoneProviderService}-defined service methods, so implementations must be defensive and not
+ * assume an ordering between them, e.g. a call to {@link #onStopUpdates()} can occur after {@link
+ * #onDestroy()} and should be handled safely.
  *
  * @hide
  */
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 725e20f..d46265e 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -68,7 +68,6 @@
 import com.android.internal.app.IVoiceInteractorRequest;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.FileDescriptor;
@@ -77,6 +76,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -1363,9 +1363,9 @@
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor resultExecutor,
             @NonNull Consumer<List<DirectAction>> callback) {
-        Preconditions.checkNotNull(activityId);
-        Preconditions.checkNotNull(resultExecutor);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(activityId);
+        Objects.requireNonNull(resultExecutor);
+        Objects.requireNonNull(callback);
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
@@ -1444,8 +1444,8 @@
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
-        Preconditions.checkNotNull(resultExecutor);
-        Preconditions.checkNotNull(resultListener);
+        Objects.requireNonNull(resultExecutor);
+        Objects.requireNonNull(resultListener);
 
         if (cancellationSignal != null) {
             cancellationSignal.throwIfCanceled();
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index f53548a..b5c75b9 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -28,6 +28,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -747,6 +748,23 @@
         return mSize;
     }
 
+    /**
+     * Performs the given action for all elements in the stored order. This implementation overrides
+     * the default implementation to avoid using the {@link #iterator()}.
+     *
+     * @param action The action to be performed for each element
+     */
+    @Override
+    public void forEach(Consumer<? super E> action) {
+        if (action == null) {
+            throw new NullPointerException("action must not be null");
+        }
+
+        for (int i = 0; i < mSize; ++i) {
+            action.accept(valueAt(i));
+        }
+    }
+
     @Override
     public Object[] toArray() {
         Object[] result = new Object[mSize];
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index 3287c27..590ba1a 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -157,4 +157,28 @@
             }
         }
     }
+
+    /**
+     * @param <K> Any class
+     * @param <V> Any class
+     * @hide
+     */
+    public interface TriConsumer<K, V> {
+        /** Consume the int-K-V tuple. */
+        void accept(int key, K mapKey, V value);
+    }
+
+    /**
+     * Iterate through all int-K pairs and operate on all of the values.
+     * @hide
+     */
+    public void forEach(@NonNull TriConsumer<K, V> consumer) {
+        for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) {
+            final int i = mData.keyAt(iIdx);
+            final ArrayMap<K, V> data = mData.valueAt(i);
+            for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) {
+                consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx));
+            }
+        }
+    }
 }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 73f7543..35c602a 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -23,11 +23,12 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.os.Build;
 import android.os.Trace;
 import android.util.jar.StrictJarFile;
@@ -52,7 +53,7 @@
 
 /**
  * Facade class that takes care of the details of APK verification on
- * behalf of PackageParser.
+ * behalf of ParsingPackageUtils.
  *
  * @hide for internal use only.
  */
@@ -62,90 +63,85 @@
 
     /**
      * Verifies the provided APK and returns the certificates associated with each signer.
-     *
-     * @throws PackageParserException if the APK's signature failed to verify.
      */
-    public static PackageParser.SigningDetails verify(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion)
-            throws PackageParserException {
-        return verifySignatures(apkPath, minSignatureSchemeVersion, true);
+    public static ParseResult<SigningDetails> verify(ParseInput input, String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion) {
+        return verifySignatures(input, apkPath, minSignatureSchemeVersion, true /* verifyFull */);
     }
 
     /**
      * Returns the certificates associated with each signer for the given APK without verification.
      * This method is dangerous and should not be used, unless the caller is absolutely certain the
      * APK is trusted.
-     *
-     * @throws PackageParserException if there was a problem collecting certificates.
      */
-    public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
-            String apkPath, int minSignatureSchemeVersion)
-            throws PackageParserException {
-        return verifySignatures(apkPath, minSignatureSchemeVersion, false);
+    public static ParseResult<SigningDetails> unsafeGetCertsWithoutVerification(
+            ParseInput input, String apkPath, int minSignatureSchemeVersion) {
+        return verifySignatures(input, apkPath, minSignatureSchemeVersion, false /* verifyFull */);
     }
 
     /**
      * Verifies the provided APK using all allowed signing schemas.
      * @return the certificates associated with each signer.
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
-     * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifySignatures(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
-            throws PackageParserException {
-        return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
-                verifyFull).signingDetails;
+    private static ParseResult<SigningDetails> verifySignatures(ParseInput input, String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) {
+        final ParseResult<SigningDetailsWithDigests> result =
+                verifySignaturesInternal(input, apkPath, minSignatureSchemeVersion, verifyFull);
+        if (result.isError()) {
+            return input.error(result);
+        }
+        return input.success(result.getResult().signingDetails);
     }
 
     /**
      * Verifies the provided APK using all allowed signing schemas.
      * @return the certificates associated with each signer and content digests.
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
-     * @throws PackageParserException if there was a problem collecting certificates
      * @hide
      */
-    public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
-            throws PackageParserException {
+    public static ParseResult<SigningDetailsWithDigests> verifySignaturesInternal(ParseInput input,
+            String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+            boolean verifyFull) {
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
-            // V3 and before are older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            // V4 and before are older than the requested minimum signing version
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
                             + " or newer for package " + apkPath);
         }
 
         // first try v4
         try {
-            return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull);
+            return verifyV4Signature(input, apkPath, minSignatureSchemeVersion, verifyFull);
         } catch (SignatureNotFoundException e) {
             // not signed with v4, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v4 signature in package " + apkPath, e);
             }
         }
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
             // V3 and before are older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
                             + " or newer for package " + apkPath);
         }
 
-        return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
+        return verifyV3AndBelowSignatures(input, apkPath, minSignatureSchemeVersion, verifyFull);
     }
 
-    private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
-            throws PackageParserException {
+    private static ParseResult<SigningDetailsWithDigests> verifyV3AndBelowSignatures(
+            ParseInput input, String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+            boolean verifyFull) {
         // try v3
         try {
-            return verifyV3Signature(apkPath, verifyFull);
+            return verifyV3Signature(input, apkPath, verifyFull);
         } catch (SignatureNotFoundException e) {
             // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
             }
         }
@@ -153,18 +149,18 @@
         // redundant, protective version check
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
             // V2 and before are older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
                             + " or newer for package " + apkPath);
         }
 
         // try v2
         try {
-            return verifyV2Signature(apkPath, verifyFull);
+            return verifyV2Signature(input, apkPath, verifyFull);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v2 signature in package " + apkPath, e);
             }
         }
@@ -172,13 +168,13 @@
         // redundant, protective version check
         if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
             // V1 and is older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
                             + " or newer for package " + apkPath);
         }
 
         // v2 didn't work, try jarsigner
-        return verifyV1Signature(apkPath, verifyFull);
+        return verifyV1Signature(input, apkPath, verifyFull);
     }
 
     /**
@@ -187,11 +183,10 @@
      * @param verifyFull whether to verify (V4 vs V3) or just collect certificates.
      * @return the certificates associated with each signer.
      * @throws SignatureNotFoundException if there are no V4 signatures in the APK
-     * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static SigningDetailsWithDigests verifyV4Signature(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
-            throws SignatureNotFoundException, PackageParserException {
+    private static ParseResult<SigningDetailsWithDigests> verifyV4Signature(ParseInput input,
+            String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+            boolean verifyFull) throws SignatureNotFoundException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
         try {
             ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner =
@@ -259,14 +254,14 @@
                 }
             }
 
-            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+            return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
                     SignatureSchemeVersion.SIGNING_BLOCK_V4, pastSignerSigs),
-                    vSigner.contentDigests);
+                    vSigner.contentDigests));
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
             // APK Signature Scheme v4 signature found but did not verify
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath
                             + " using APK Signature Scheme v4", e);
         } finally {
@@ -280,10 +275,9 @@
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @return the certificates associated with each signer.
      * @throws SignatureNotFoundException if there are no V3 signatures in the APK
-     * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull)
-            throws SignatureNotFoundException, PackageParserException {
+    private static ParseResult<SigningDetailsWithDigests> verifyV3Signature(ParseInput input,
+            String apkPath, boolean verifyFull) throws SignatureNotFoundException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
         try {
             ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
@@ -301,14 +295,14 @@
                     pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
-            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+            return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
                     SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
-                    vSigner.contentDigests);
+                    vSigner.contentDigests));
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
             // APK Signature Scheme v3 signature found but did not verify
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath
                             + " using APK Signature Scheme v3", e);
         } finally {
@@ -322,23 +316,22 @@
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @return the certificates associated with each signer.
      * @throws SignatureNotFoundException if there are no V2 signatures in the APK
-     * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull)
-            throws SignatureNotFoundException, PackageParserException {
+    private static ParseResult<SigningDetailsWithDigests> verifyV2Signature(ParseInput input,
+            String apkPath, boolean verifyFull) throws SignatureNotFoundException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
         try {
             ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner =
                     ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
             Certificate[][] signerCerts = vSigner.certs;
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
+            return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests));
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
             // APK Signature Scheme v2 signature found but did not verify
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath
                             + " using APK Signature Scheme v2", e);
         } finally {
@@ -350,10 +343,9 @@
      * Verifies the provided APK using JAR schema.
      * @return the certificates associated with each signer.
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
-     * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull)
-            throws PackageParserException {
+    private static ParseResult<SigningDetailsWithDigests> verifyV1Signature(ParseInput input,
+            String apkPath, boolean verifyFull) {
         StrictJarFile jarFile = null;
 
         try {
@@ -375,12 +367,17 @@
             final ZipEntry manifestEntry = jarFile.findEntry(
                     ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
             if (manifestEntry == null) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                         "Package " + apkPath + " has no manifest");
             }
-            lastCerts = loadCertificates(jarFile, manifestEntry);
+            final ParseResult<Certificate[][]> result =
+                    loadCertificates(input, jarFile, manifestEntry);
+            if (result.isError()) {
+                return input.error(result);
+            }
+            lastCerts = result.getResult();
             if (ArrayUtils.isEmpty(lastCerts)) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+                return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
                         + apkPath + " has no certificates at entry "
                         + ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
             }
@@ -401,9 +398,15 @@
                 }
 
                 for (ZipEntry entry : toVerify) {
-                    final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
+                    final Certificate[][] entryCerts;
+                    final ParseResult<Certificate[][]> ret =
+                            loadCertificates(input, jarFile, entry);
+                    if (ret.isError()) {
+                        return input.error(ret);
+                    }
+                    entryCerts = ret.getResult();
                     if (ArrayUtils.isEmpty(entryCerts)) {
-                        throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                        return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                                 "Package " + apkPath + " has no certificates at entry "
                                         + entry.getName());
                     }
@@ -411,20 +414,20 @@
                     // make sure all entries use the same signing certs
                     final Signature[] entrySigs = convertToSignatures(entryCerts);
                     if (!Signature.areExactMatch(lastSigs, entrySigs)) {
-                        throw new PackageParserException(
+                        return input.error(
                                 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                                 "Package " + apkPath + " has mismatched certificates at entry "
                                         + entry.getName());
                     }
                 }
             }
-            return new SigningDetailsWithDigests(
-                    new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
+            return input.success(new SigningDetailsWithDigests(
+                    new SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null));
         } catch (GeneralSecurityException e) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+            return input.error(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
         } catch (IOException | RuntimeException e) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+            return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath, e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -432,17 +435,17 @@
         }
     }
 
-    private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
-            throws PackageParserException {
+    private static ParseResult<Certificate[][]> loadCertificates(ParseInput input,
+            StrictJarFile jarFile, ZipEntry entry) {
         InputStream is = null;
         try {
             // We must read the stream for the JarEntry to retrieve
             // its certificates.
             is = jarFile.getInputStream(entry);
             readFullyIgnoringContents(is);
-            return jarFile.getCertificateChains(entry);
+            return input.success(jarFile.getCertificateChains(entry));
         } catch (IOException | RuntimeException e) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                     "Failed reading " + entry.getName() + " in " + jarFile, e);
         } finally {
             IoUtils.closeQuietly(is);
@@ -576,7 +579,7 @@
      * @hide for internal use only.
      */
     public static class SigningDetailsWithDigests {
-        public final PackageParser.SigningDetails signingDetails;
+        public final SigningDetails signingDetails;
 
         /**
          * APK Signature Schemes v2/v3/v4 might contain multiple content digests.
@@ -587,7 +590,7 @@
          */
         public final Map<Integer, byte[]> contentDigests;
 
-        SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+        SigningDetailsWithDigests(SigningDetails signingDetails,
                 Map<Integer, byte[]> contentDigests) {
             this.signingDetails = signingDetails;
             this.contentDigests = contentDigests;
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
index fa5477e..3ca3fc0 100644
--- a/core/java/android/util/apk/ZipUtils.java
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -63,7 +63,8 @@
         // exactly the remaining bytes in the buffer. The search is bounded because the maximum
         // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
 
-        long fileSize = zip.length();
+        // TODO(b/193592496) RandomAccessFile#length
+        long fileSize = zip.getChannel().size();
         if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
             return null;
         }
@@ -110,7 +111,8 @@
             throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize);
         }
 
-        long fileSize = zip.length();
+        // TODO(b/193592496) RandomAccessFile#length
+        long fileSize = zip.getChannel().size();
         if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
             // No space for EoCD record in the file.
             return null;
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
deleted file mode 100644
index 4696ae3..0000000
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ /dev/null
@@ -1,187 +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.util.imetracing;
-
-import android.annotation.Nullable;
-import android.app.ActivityThread;
-import android.content.Context;
-import android.inputmethodservice.AbstractInputMethodService;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.view.IInputMethodManager;
-
-import java.io.PrintWriter;
-
-/**
- *
- * An abstract class that declares the methods for ime trace related operations - enable trace,
- * schedule trace and add new trace to buffer. Both the client and server side classes can use
- * it by getting an implementation through {@link ImeTracing#getInstance()}.
- *
- * @hide
- */
-public abstract class ImeTracing {
-
-    static final String TAG = "imeTracing";
-    public static final String PROTO_ARG = "--proto-com-android-imetracing";
-
-    /* Constants describing the component type that triggered a dump. */
-    public static final int IME_TRACING_FROM_CLIENT = 0;
-    public static final int IME_TRACING_FROM_IMS = 1;
-    public static final int IME_TRACING_FROM_IMMS = 2;
-
-    private static ImeTracing sInstance;
-    static boolean sEnabled = false;
-    IInputMethodManager mService;
-
-    protected boolean mDumpInProgress;
-    protected final Object mDumpInProgressLock = new Object();
-
-    ImeTracing() throws ServiceNotFoundException {
-        mService = IInputMethodManager.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
-    }
-
-    /**
-     * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class
-     * and an instance of {@link ImeTracingClientImpl} when called from a client side class.
-     * Useful to schedule a dump for next frame or save a dump when certain methods are called.
-     *
-     * @return Instance of one of the children classes of {@link ImeTracing}
-     */
-    public static ImeTracing getInstance() {
-        if (sInstance == null) {
-            try {
-                sInstance = isSystemProcess()
-                        ? new ImeTracingServerImpl() : new ImeTracingClientImpl();
-            } catch (RemoteException | ServiceNotFoundException e) {
-                Log.e(TAG, "Exception while creating ImeTracing instance", e);
-            }
-        }
-        return sInstance;
-    }
-
-    /**
-     * Transmits the information from client or InputMethodService side to the server, in order to
-     * be stored persistently to the current IME tracing dump.
-     *
-     * @param protoDump client or service side information to be stored by the server
-     * @param source where the information is coming from, refer to {@see #IME_TRACING_FROM_CLIENT}
-     * and {@see #IME_TRACING_FROM_IMS}
-     * @param where
-     */
-    public void sendToService(byte[] protoDump, int source, String where) throws RemoteException {
-        mService.startProtoDump(protoDump, source, where);
-    }
-
-    /**
-     * @param proto dump to be added to the buffer
-     */
-    public abstract void addToBuffer(ProtoOutputStream proto, int source);
-
-    /**
-     * Starts a proto dump of the client side information.
-     *
-     * @param where Place where the trace was triggered.
-     * @param immInstance The {@link InputMethodManager} instance to dump.
-     * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
-     */
-    public abstract void triggerClientDump(String where, InputMethodManager immInstance,
-            ProtoOutputStream icProto);
-
-    /**
-     * Starts a proto dump of the currently connected InputMethodService information.
-     *
-     * @param where Place where the trace was triggered.
-     * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped.
-     * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
-     */
-    public abstract void triggerServiceDump(String where, AbstractInputMethodService service,
-            ProtoOutputStream icProto);
-
-    /**
-     * Starts a proto dump of the InputMethodManagerService information.
-     *
-     * @param where Place where the trace was triggered.
-     */
-    public abstract void triggerManagerServiceDump(String where);
-
-    /**
-     * Being called while taking a bugreport so that tracing files can be included in the bugreport
-     * when the IME tracing is running.  Does nothing otherwise.
-     *
-     * @param pw Print writer
-     */
-    public void saveForBugreport(@Nullable PrintWriter pw) {
-        // does nothing by default.
-    }
-
-    /**
-     * Sets whether ime tracing is enabled.
-     *
-     * @param enabled Tells whether ime tracing should be enabled or disabled.
-     */
-    public void setEnabled(boolean enabled) {
-        sEnabled = enabled;
-    }
-
-    /**
-     * @return {@code true} if dumping is enabled, {@code false} otherwise.
-     */
-    public boolean isEnabled() {
-        return sEnabled;
-    }
-
-    /**
-     * @return {@code true} if tracing is available, {@code false} otherwise.
-     */
-    public boolean isAvailable() {
-        return mService != null;
-    }
-
-    /**
-     * Starts a new IME trace if one is not already started.
-     *
-     * @param pw Print writer
-     */
-    public abstract void startTrace(@Nullable PrintWriter pw);
-
-    /**
-     * Stops the IME trace if one was previously started and writes the current buffers to disk.
-     *
-     * @param pw Print writer
-     */
-    public abstract void stopTrace(@Nullable PrintWriter pw);
-
-    private static boolean isSystemProcess() {
-        return ActivityThread.isSystem();
-    }
-
-    protected void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Log.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
-}
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
deleted file mode 100644
index 5a57a6a..0000000
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.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 android.util.imetracing;
-
-import android.annotation.NonNull;
-import android.inputmethodservice.AbstractInputMethodService;
-import android.os.RemoteException;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-import android.view.inputmethod.InputMethodManager;
-
-import java.io.PrintWriter;
-
-/**
- * @hide
- */
-class ImeTracingClientImpl extends ImeTracing {
-    ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
-        sEnabled = mService.isImeTraceEnabled();
-    }
-
-    @Override
-    public void addToBuffer(ProtoOutputStream proto, int source) {
-    }
-
-    @Override
-    public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
-            ProtoOutputStream icProto) {
-        if (!isEnabled() || !isAvailable()) {
-            return;
-        }
-
-        synchronized (mDumpInProgressLock) {
-            if (mDumpInProgress) {
-                return;
-            }
-            mDumpInProgress = true;
-        }
-
-        try {
-            ProtoOutputStream proto = new ProtoOutputStream();
-            immInstance.dumpDebug(proto, icProto);
-            sendToService(proto.getBytes(), IME_TRACING_FROM_CLIENT, where);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception while sending ime-related client dump to server", e);
-        } finally {
-            mDumpInProgress = false;
-        }
-    }
-
-    @Override
-    public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service,
-            ProtoOutputStream icProto) {
-        if (!isEnabled() || !isAvailable()) {
-            return;
-        }
-
-        synchronized (mDumpInProgressLock) {
-            if (mDumpInProgress) {
-                return;
-            }
-            mDumpInProgress = true;
-        }
-
-        try {
-            ProtoOutputStream proto = new ProtoOutputStream();
-            service.dumpProtoInternal(proto, icProto);
-            sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception while sending ime-related service dump to server", e);
-        } finally {
-            mDumpInProgress = false;
-        }
-    }
-
-    @Override
-    public void triggerManagerServiceDump(String where) {
-        // Intentionally left empty, this is implemented in ImeTracingServerImpl
-    }
-
-    @Override
-    public void startTrace(PrintWriter pw) {
-    }
-
-    @Override
-    public void stopTrace(PrintWriter pw) {
-    }
-}
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
deleted file mode 100644
index 06e4c50..0000000
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ /dev/null
@@ -1,239 +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.util.imetracing;
-
-import static android.os.Build.IS_USER;
-
-import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
-import android.os.RemoteException;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
-import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodManagerServiceTraceFileProto;
-import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceFileProto;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.TraceBuffer;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-/**
- * @hide
- */
-class ImeTracingServerImpl extends ImeTracing {
-    private static final String TRACE_DIRNAME = "/data/misc/wmtrace/";
-    private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.pb";
-    private static final String TRACE_FILENAME_IMS = "ime_trace_service.pb";
-    private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.pb";
-    private static final int BUFFER_CAPACITY = 4096 * 1024;
-
-    // Needed for winscope to auto-detect the dump type. Explained further in
-    // core.proto.android.view.inputmethod.inputmethodeditortrace.proto.
-    // This magic number corresponds to InputMethodClientsTraceFileProto.
-    private static final long MAGIC_NUMBER_CLIENTS_VALUE =
-            ((long) InputMethodClientsTraceFileProto.MAGIC_NUMBER_H << 32)
-                | InputMethodClientsTraceFileProto.MAGIC_NUMBER_L;
-    // This magic number corresponds to InputMethodServiceTraceFileProto.
-    private static final long MAGIC_NUMBER_IMS_VALUE =
-            ((long) InputMethodServiceTraceFileProto.MAGIC_NUMBER_H << 32)
-                | InputMethodServiceTraceFileProto.MAGIC_NUMBER_L;
-    // This magic number corresponds to InputMethodManagerServiceTraceFileProto.
-    private static final long MAGIC_NUMBER_IMMS_VALUE =
-            ((long) InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_H << 32)
-                | InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_L;
-
-    private final TraceBuffer mBufferClients;
-    private final File mTraceFileClients;
-    private final TraceBuffer mBufferIms;
-    private final File mTraceFileIms;
-    private final TraceBuffer mBufferImms;
-    private final File mTraceFileImms;
-
-    private final Object mEnabledLock = new Object();
-
-    ImeTracingServerImpl() throws ServiceNotFoundException {
-        mBufferClients = new TraceBuffer<>(BUFFER_CAPACITY);
-        mTraceFileClients = new File(TRACE_DIRNAME + TRACE_FILENAME_CLIENTS);
-        mBufferIms = new TraceBuffer<>(BUFFER_CAPACITY);
-        mTraceFileIms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMS);
-        mBufferImms = new TraceBuffer<>(BUFFER_CAPACITY);
-        mTraceFileImms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMMS);
-    }
-
-    /**
-     * The provided dump is added to the corresponding dump buffer:
-     * {@link ImeTracingServerImpl#mBufferClients} or {@link ImeTracingServerImpl#mBufferIms}.
-     *
-     * @param proto dump to be added to the buffer
-     */
-    @Override
-    public void addToBuffer(ProtoOutputStream proto, int source) {
-        if (isAvailable() && isEnabled()) {
-            switch (source) {
-                case IME_TRACING_FROM_CLIENT:
-                    mBufferClients.add(proto);
-                    return;
-                case IME_TRACING_FROM_IMS:
-                    mBufferIms.add(proto);
-                    return;
-                case IME_TRACING_FROM_IMMS:
-                    mBufferImms.add(proto);
-                    return;
-                default:
-                    // Source not recognised.
-                    Log.w(TAG, "Request to add to buffer, but source not recognised.");
-            }
-        }
-    }
-
-    @Override
-    public void triggerClientDump(String where, InputMethodManager immInstance,
-            ProtoOutputStream icProto) {
-        // Intentionally left empty, this is implemented in ImeTracingClientImpl
-    }
-
-    @Override
-    public void triggerServiceDump(String where, AbstractInputMethodService service,
-            ProtoOutputStream icProto) {
-        // Intentionally left empty, this is implemented in ImeTracingClientImpl
-    }
-
-    @Override
-    public void triggerManagerServiceDump(String where) {
-        if (!isEnabled() || !isAvailable()) {
-            return;
-        }
-
-        synchronized (mDumpInProgressLock) {
-            if (mDumpInProgress) {
-                return;
-            }
-            mDumpInProgress = true;
-        }
-
-        try {
-            sendToService(null, IME_TRACING_FROM_IMMS, where);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception while sending ime-related manager service dump to server", e);
-        } finally {
-            mDumpInProgress = false;
-        }
-    }
-
-    private void writeTracesToFilesLocked() {
-        try {
-            ProtoOutputStream clientsProto = new ProtoOutputStream();
-            clientsProto.write(InputMethodClientsTraceFileProto.MAGIC_NUMBER,
-                    MAGIC_NUMBER_CLIENTS_VALUE);
-            mBufferClients.writeTraceToFile(mTraceFileClients, clientsProto);
-
-            ProtoOutputStream imsProto = new ProtoOutputStream();
-            imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER, MAGIC_NUMBER_IMS_VALUE);
-            mBufferIms.writeTraceToFile(mTraceFileIms, imsProto);
-
-            ProtoOutputStream immsProto = new ProtoOutputStream();
-            immsProto.write(InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER,
-                    MAGIC_NUMBER_IMMS_VALUE);
-            mBufferImms.writeTraceToFile(mTraceFileImms, immsProto);
-
-            resetBuffers();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write buffer to file", e);
-        }
-    }
-
-    @GuardedBy("mEnabledLock")
-    @Override
-    public void startTrace(@Nullable PrintWriter pw) {
-        if (IS_USER) {
-            Log.w(TAG, "Warn: Tracing is not supported on user builds.");
-            return;
-        }
-
-        synchronized (mEnabledLock) {
-            if (isAvailable() && isEnabled()) {
-                Log.w(TAG, "Warn: Tracing is already started.");
-                return;
-            }
-
-            logAndPrintln(pw, "Starting tracing in " + TRACE_DIRNAME + ": " + TRACE_FILENAME_CLIENTS
-                    + ", " + TRACE_FILENAME_IMS + ", " + TRACE_FILENAME_IMMS);
-            sEnabled = true;
-            resetBuffers();
-        }
-    }
-
-    @Override
-    public void stopTrace(@Nullable PrintWriter pw) {
-        if (IS_USER) {
-            Log.w(TAG, "Warn: Tracing is not supported on user builds.");
-            return;
-        }
-
-        synchronized (mEnabledLock) {
-            if (!isAvailable() || !isEnabled()) {
-                Log.w(TAG, "Warn: Tracing is not available or not started.");
-                return;
-            }
-
-            logAndPrintln(pw, "Stopping tracing and writing traces in " + TRACE_DIRNAME + ": "
-                    + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", "
-                    + TRACE_FILENAME_IMMS);
-            sEnabled = false;
-            writeTracesToFilesLocked();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void saveForBugreport(@Nullable PrintWriter pw) {
-        if (IS_USER) {
-            return;
-        }
-        synchronized (mEnabledLock) {
-            if (!isAvailable() || !isEnabled()) {
-                return;
-            }
-            // Temporarily stop accepting logs from trace event providers.  There is a small chance
-            // that we may drop some trace events while writing the file, but we currently need to
-            // live with that.  Note that addToBuffer() also has a bug that it doesn't do
-            // read-acquire so flipping sEnabled here doesn't even guarantee that addToBuffer() will
-            // temporarily stop accepting incoming events...
-            // TODO(b/175761228): Implement atomic snapshot to avoid downtime.
-            // TODO(b/175761228): Fix synchronization around sEnabled.
-            sEnabled = false;
-            logAndPrintln(pw, "Writing traces in " + TRACE_DIRNAME + ": "
-                    + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", "
-                    + TRACE_FILENAME_IMMS);
-            writeTracesToFilesLocked();
-            sEnabled = true;
-        }
-    }
-
-    private void resetBuffers() {
-        mBufferClients.resetBuffer();
-        mBufferIms.resetBuffer();
-        mBufferImms.resetBuffer();
-    }
-}
diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/android/util/imetracing/InputConnectionHelper.java
deleted file mode 100644
index 39f1e01..0000000
--- a/core/java/android/util/imetracing/InputConnectionHelper.java
+++ /dev/null
@@ -1,231 +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.util.imetracing;
-
-import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE;
-import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT;
-import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT;
-import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT;
-import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR;
-import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR;
-import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.proto.ProtoOutputStream;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode;
-import android.view.inputmethod.InputConnectionCallProto.GetExtractedText;
-import android.view.inputmethod.InputConnectionCallProto.GetSelectedText;
-import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText;
-import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor;
-import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor;
-import android.view.inputmethod.SurroundingText;
-
-/**
- * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are
- * integrated into {@link ImeTracing}.
- * @hide
- */
-public class InputConnectionHelper {
-    static final String TAG = "InputConnectionHelper";
-    public static final boolean DUMP_TEXT = false;
-
-    private InputConnectionHelper() {}
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data.
-     *
-     * @param length The expected length of the text. This must be non-negative.
-     * @param flags Supplies additional options controlling how the text is
-     * returned. May be either {@code 0} or
-     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
-     * @param result The text after the cursor position; the length of the
-     * returned text might be less than <var>length</var>.
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length,
-            int flags, @Nullable CharSequence result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_TEXT_AFTER_CURSOR);
-        proto.write(GetTextAfterCursor.LENGTH, length);
-        proto.write(GetTextAfterCursor.FLAGS, flags);
-        if (result == null) {
-            proto.write(GetTextAfterCursor.RESULT, "null result");
-        } else if (DUMP_TEXT) {
-            proto.write(GetTextAfterCursor.RESULT, result.toString());
-        }
-        proto.end(token);
-        return proto;
-    }
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data.
-     *
-     * @param length The expected length of the text. This must be non-negative.
-     * @param flags Supplies additional options controlling how the text is
-     * returned. May be either {@code 0} or
-     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
-     * @param result The text before the cursor position; the length of the
-     * returned text might be less than <var>length</var>.
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
-            int flags, @Nullable CharSequence result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_TEXT_BEFORE_CURSOR);
-        proto.write(GetTextBeforeCursor.LENGTH, length);
-        proto.write(GetTextBeforeCursor.FLAGS, flags);
-        if (result == null) {
-            proto.write(GetTextBeforeCursor.RESULT, "null result");
-        } else if (DUMP_TEXT) {
-            proto.write(GetTextBeforeCursor.RESULT, result.toString());
-        }
-        proto.end(token);
-        return proto;
-    }
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data.
-     *
-     * @param flags Supplies additional options controlling how the text is
-     * returned. May be either {@code 0} or
-     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
-     * @param result the text that is currently selected, if any, or null if
-     * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
-     * later, returns false when the target application does not implement
-     * this method.
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetSelectedTextProto(int flags,
-            @Nullable CharSequence result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_SELECTED_TEXT);
-        proto.write(GetSelectedText.FLAGS, flags);
-        if (result == null) {
-            proto.write(GetSelectedText.RESULT, "null result");
-        } else if (DUMP_TEXT) {
-            proto.write(GetSelectedText.RESULT, result.toString());
-        }
-        proto.end(token);
-        return proto;
-    }
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data.
-     *
-     * @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 {@code 0} or
-     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
-     * @param result 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> .
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0)
-            int beforeLength, @IntRange(from = 0) int afterLength, int flags,
-            @Nullable SurroundingText result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_SURROUNDING_TEXT);
-        proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength);
-        proto.write(GetSurroundingText.AFTER_LENGTH, afterLength);
-        proto.write(GetSurroundingText.FLAGS, flags);
-        if (result == null) {
-            final long token_result = proto.start(GetSurroundingText.RESULT);
-            proto.write(GetSurroundingText.SurroundingText.TEXT, "null result");
-            proto.end(token_result);
-        } else if (DUMP_TEXT) {
-            final long token_result = proto.start(GetSurroundingText.RESULT);
-            proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString());
-            proto.write(GetSurroundingText.SurroundingText.SELECTION_START,
-                    result.getSelectionStart());
-            proto.write(GetSurroundingText.SurroundingText.SELECTION_END,
-                    result.getSelectionEnd());
-            proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset());
-            proto.end(token_result);
-        }
-        proto.end(token);
-        return proto;
-    }
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data.
-     *
-     * @param reqModes The desired modes to retrieve, as defined by
-     * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.
-     * @param result the caps mode flags that are in effect at the current
-     * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_CURSOR_CAPS_MODE);
-        proto.write(GetCursorCapsMode.REQ_MODES, reqModes);
-        if (DUMP_TEXT) {
-            proto.write(GetCursorCapsMode.RESULT, result);
-        }
-        proto.end(token);
-        return proto;
-    }
-
-    /**
-     * Builder for InputConnectionCallProto to hold
-     * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)}
-     * data.
-     *
-     * @param request Description of how the text should be returned.
-     * {@link android.view.inputmethod.ExtractedTextRequest}
-     * @param flags Additional options to control the client, either {@code 0} or
-     * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}.
-     * @param result an {@link android.view.inputmethod.ExtractedText}
-     * object describing the state of the text view and containing the
-     * extracted text itself, or null if the input connection is no
-     * longer valid of the editor can't comply with the request for
-     * some reason.
-     * @return ProtoOutputStream holding the InputConnectionCallProto data.
-     */
-    public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest
-            request, int flags, @Nullable ExtractedText result) {
-        ProtoOutputStream proto = new ProtoOutputStream();
-        final long token = proto.start(GET_EXTRACTED_TEXT);
-        final long token_request = proto.start(REQUEST);
-        proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token);
-        proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags);
-        proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines);
-        proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars);
-        proto.end(token_request);
-        proto.write(GetExtractedText.FLAGS, flags);
-        if (result == null) {
-            proto.write(GetExtractedText.RESULT, "null result");
-        } else if (DUMP_TEXT) {
-            proto.write(GetExtractedText.RESULT, result.text.toString());
-        }
-        proto.end(token);
-        return proto;
-    }
-}
diff --git a/core/java/android/util/imetracing/OWNERS b/core/java/android/util/imetracing/OWNERS
deleted file mode 100644
index 885fd0a..0000000
--- a/core/java/android/util/imetracing/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-set noparent
-
-include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 199ecb3..d23200b 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -616,9 +616,12 @@
                         // focus instead fetching all provider nodes to do the search here.
                         AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
                         if (provider != null) {
-                            if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
-                                focused = AccessibilityNodeInfo.obtain(
-                                        mViewRootImpl.mAccessibilityFocusedVirtualView);
+                            final AccessibilityNodeInfo focusNode =
+                                    mViewRootImpl.mAccessibilityFocusedVirtualView;
+                            if (focusNode != null) {
+                                final int virtualNodeId = AccessibilityNodeInfo
+                                        .getVirtualDescendantId(focusNode.getSourceNodeId());
+                                focused = provider.createAccessibilityNodeInfo(virtualNodeId);
                             }
                         } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
                             focused = host.createAccessibilityNodeInfo();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8021636..f4539c2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -814,9 +814,10 @@
      * @param displayId The display associated with the window context
      * @param options A bundle used to pass window-related options and choose the right DisplayArea
      *
-     * @return {@code true} if the WindowContext is attached to the DisplayArea successfully.
+     * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
+     * attached to the DisplayArea successfully. {@code null}, otherwise.
      */
-    boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
+    Configuration attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
             in Bundle options);
 
     /**
@@ -865,4 +866,11 @@
     void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
 
     boolean isTaskSnapshotSupported();
+
+    /**
+     * Returns the preferred display ID to show software keyboard.
+     *
+     * @see android.window.WindowProviderService#getLaunchedDisplayId
+     */
+    int getImeDisplayId();
 }
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 5a34a92..bd97ef0 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import static android.view.Display.INVALID_DISPLAY;
-
 import android.annotation.Nullable;
 import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
 import android.os.IBinder;
-import android.os.TouchOcclusionMode;
 
 import java.lang.ref.WeakReference;
 
@@ -101,10 +99,6 @@
     // Display this input is on.
     public int displayId;
 
-    // If this value is set to a valid display ID, it indicates this window is a portal which
-    // transports the touch of this window to the display indicated by portalToDisplayId.
-    public int portalToDisplayId = INVALID_DISPLAY;
-
     /**
      * Crops the touchable region to the bounds of the surface provided.
      *
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f915c9..1a9b34e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -45,7 +45,6 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
@@ -61,6 +60,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.inputmethod.ImeTracing;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ee33541..bf9de39 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -36,13 +36,13 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.util.Log;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.ImeTracing;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 69ff64f..40942ea7 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -4008,6 +4008,22 @@
         public float orientation;
 
         /**
+         * The movement of x position of a motion event.
+         *
+         * @see MotionEvent#AXIS_RELATIVE_X
+         * @hide
+         */
+        public float relativeX;
+
+        /**
+         * The movement of y position of a motion event.
+         *
+         * @see MotionEvent#AXIS_RELATIVE_Y
+         * @hide
+         */
+        public float relativeY;
+
+        /**
          * Clears the contents of this object.
          * Resets all axes to zero.
          */
@@ -4023,6 +4039,8 @@
             toolMajor = 0;
             toolMinor = 0;
             orientation = 0;
+            relativeX = 0;
+            relativeY = 0;
         }
 
         /**
@@ -4053,6 +4071,8 @@
             toolMajor = other.toolMajor;
             toolMinor = other.toolMinor;
             orientation = other.orientation;
+            relativeX = other.relativeX;
+            relativeY = other.relativeY;
         }
 
         /**
@@ -4084,6 +4104,10 @@
                     return toolMinor;
                 case AXIS_ORIENTATION:
                     return orientation;
+                case AXIS_RELATIVE_X:
+                    return relativeX;
+                case AXIS_RELATIVE_Y:
+                    return relativeY;
                 default: {
                     if (axis < 0 || axis > 63) {
                         throw new IllegalArgumentException("Axis out of range.");
@@ -4137,6 +4161,12 @@
                 case AXIS_ORIENTATION:
                     orientation = value;
                     break;
+                case AXIS_RELATIVE_X:
+                    relativeX = value;
+                    break;
+                case AXIS_RELATIVE_Y:
+                    relativeY = value;
+                    break;
                 default: {
                     if (axis < 0 || axis > 63) {
                         throw new IllegalArgumentException("Axis out of range.");
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index a78036f..e1cc604 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.app.ActivityOptions;
+import android.app.IApplicationThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -58,6 +59,9 @@
     private int mCallingPid;
     private int mCallingUid;
 
+    /** @see #getCallingApplication */
+    private IApplicationThread mCallingApplication;
+
     /**
      * @param runner The interface that gets notified when we actually need to start the animation.
      * @param duration The duration of the animation.
@@ -81,11 +85,19 @@
         this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
     }
 
+    @UnsupportedAppUsage
+    public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+            long statusBarTransitionDelay, IApplicationThread callingApplication) {
+        this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
+        mCallingApplication = callingApplication;
+    }
+
     public RemoteAnimationAdapter(Parcel in) {
         mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
         mDuration = in.readLong();
         mStatusBarTransitionDelay = in.readLong();
         mChangeNeedsSnapshot = in.readBoolean();
+        mCallingApplication = IApplicationThread.Stub.asInterface(in.readStrongBinder());
     }
 
     public IRemoteAnimationRunner getRunner() {
@@ -126,6 +138,15 @@
         return mCallingUid;
     }
 
+    /**
+     * Gets the ApplicationThread that will run the animation. Instead it is intended to pass the
+     * calling information among client processes (eg. shell + launcher) through one-way binder
+     * calls (where binder itself doesn't track calling information).
+     */
+    public IApplicationThread getCallingApplication() {
+        return mCallingApplication;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -137,6 +158,7 @@
         dest.writeLong(mDuration);
         dest.writeLong(mStatusBarTransitionDelay);
         dest.writeBoolean(mChangeNeedsSnapshot);
+        dest.writeStrongInterface(mCallingApplication);
     }
 
     public static final @android.annotation.NonNull Creator<RemoteAnimationAdapter> CREATOR
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 4e2f37f..b300f30 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -162,6 +162,8 @@
             IBinder displayToken, long nativeSurfaceObject);
     private static native void nativeSetDisplayLayerStack(long transactionObj,
             IBinder displayToken, int layerStack);
+    private static native void nativeSetDisplayFlags(long transactionObj,
+            IBinder displayToken, int flags);
     private static native void nativeSetDisplayProjection(long transactionObj,
             IBinder displayToken, int orientation,
             int l, int t, int r, int b,
@@ -548,6 +550,15 @@
      */
     private static final int SURFACE_OPAQUE = 0x02;
 
+    /* flags used with setDisplayFlags() (keep in sync with DisplayDevice.h) */
+
+    /**
+     * DisplayDevice flag: This display's transform is sent to inputflinger and used for input
+     * dispatch. This flag is used to disambiguate displays which share a layerstack.
+     * @hide
+     */
+    public static final int DISPLAY_RECEIVES_INPUT = 0x01;
+
     // Display power modes.
     /**
      * Display power mode off: used while blanking the screen.
@@ -3167,6 +3178,17 @@
         /**
          * @hide
          */
+        public Transaction setDisplayFlags(IBinder displayToken, int flags) {
+            if (displayToken == null) {
+                throw new IllegalArgumentException("displayToken must not be null");
+            }
+            nativeSetDisplayFlags(mNativeObject, displayToken, flags);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
         public Transaction setDisplayProjection(IBinder displayToken,
                 int orientation, Rect layerStackRect, Rect displayRect) {
             if (displayToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f21d855dd..c39426d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1565,12 +1565,21 @@
      * @hide
      */
     public void setResizeBackgroundColor(int bgColor) {
+        setResizeBackgroundColor(mTmpTransaction, bgColor);
+        mTmpTransaction.apply();
+    }
+
+    /**
+     * Version of {@link #setResizeBackgroundColor(int)} that allows you to provide
+     * {@link SurfaceControl.Transaction}.
+     * @hide
+     */
+    public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) {
         if (mBackgroundControl == null) {
             return;
         }
-
         mBackgroundColor = bgColor;
-        updateBackgroundColor(mTmpTransaction).apply();
+        updateBackgroundColor(t);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f4223fb..f3ebe5b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6432,7 +6432,7 @@
 
     @Override
     public String toString() {
-        StringBuilder out = new StringBuilder(128);
+        StringBuilder out = new StringBuilder(256);
         out.append(getClass().getName());
         out.append('{');
         out.append(Integer.toHexString(System.identityHashCode(this)));
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 679da31..488f7c3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2723,7 +2723,7 @@
                                     continue;
                                 }
                                 childWithAccessibilityFocus = null;
-                                i = childrenCount - 1;
+                                i = childrenCount;
                             }
 
                             if (!child.canReceivePointerEvents()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3550a31..b9346b5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -151,7 +151,6 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.InputDevice.InputSourceClass;
 import android.view.InsetsState.InternalInsetsType;
@@ -192,6 +191,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
@@ -253,11 +253,11 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
-     * Whether or not to report end-to-end input latency. Disabled temporarily as a
+     * Whether or not to report end-to-end input latency. Can be disabled temporarily as a
      * risk mitigation against potential jank caused by acquiring a weak reference
-     * per frame
+     * per frame.
      */
-    private static final boolean ENABLE_INPUT_LATENCY_TRACKING = false;
+    private static final boolean ENABLE_INPUT_LATENCY_TRACKING = true;
 
     /**
      * Set this system property to true to force the view hierarchy to render
@@ -291,6 +291,21 @@
      */
     private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
 
+    /**
+     * If set to {@code true}, the new logic to layout system bars as normal window and to use
+     * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will
+     * be applied.
+     */
+    private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets";
+
+    /**
+     * A flag to indicate to use the new generalized insets window logic, or the old hard-coded
+     * insets window layout logic.
+     * {@hide}
+     */
+    public static final boolean INSETS_LAYOUT_GENERALIZATION =
+            SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, false);
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -388,6 +403,8 @@
     View mView;
 
     View mAccessibilityFocusedHost;
+    // Accessibility-focused virtual view. The bounds and sourceNodeId of
+    // mAccessibilityFocusedVirtualView is up-to-date while other fields may be stale.
     AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
 
     // True if the window currently has pointer capture enabled.
@@ -4813,6 +4830,9 @@
     }
 
     /**
+     * Get accessibility-focused virtual view. The bounds and sourceNodeId of the returned node is
+     * up-to-date while other fields may be stale.
+     *
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 57dfc62..1edbbba 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.app.ActivityTaskManager;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -51,6 +52,7 @@
     public boolean inPictureInPicture;
     public boolean hasFlagWatchOutsideTouch;
     public int displayId = Display.INVALID_DISPLAY;
+    public int taskId = ActivityTaskManager.INVALID_TASK_ID;
 
     private WindowInfo() {
         /* do nothing - hide constructor */
@@ -67,6 +69,7 @@
     public static WindowInfo obtain(WindowInfo other) {
         WindowInfo window = obtain();
         window.displayId = other.displayId;
+        window.taskId = other.taskId;
         window.type = other.type;
         window.layer = other.layer;
         window.token = other.token;
@@ -103,6 +106,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(displayId);
+        parcel.writeInt(taskId);
         parcel.writeInt(type);
         parcel.writeInt(layer);
         parcel.writeStrongBinder(token);
@@ -129,6 +133,7 @@
         builder.append("WindowInfo[");
         builder.append("title=").append(title);
         builder.append(", displayId=").append(displayId);
+        builder.append(", taskId=").append(taskId);
         builder.append(", type=").append(type);
         builder.append(", layer=").append(layer);
         builder.append(", token=").append(token);
@@ -146,6 +151,7 @@
 
     private void initFromParcel(Parcel parcel) {
         displayId = parcel.readInt();
+        taskId = parcel.readInt();
         type = parcel.readInt();
         layer = parcel.readInt();
         token = parcel.readStrongBinder();
@@ -169,6 +175,7 @@
 
     private void clear() {
         displayId = Display.INVALID_DISPLAY;
+        taskId = ActivityTaskManager.INVALID_TASK_ID;
         type = 0;
         layer = 0;
         token = null;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 55beae0f..fcfb0ab 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -97,6 +97,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -394,6 +395,11 @@
      */
     int TRANSIT_KEYGUARD_UNOCCLUDE = 9;
     /**
+     * A window is starting to enter PiP.
+     * @hide
+     */
+    int TRANSIT_PIP = 10;
+    /**
      * The first slot for custom transition types. Callers (like Shell) can make use of custom
      * transition types for dealing with special cases. These types are effectively ignored by
      * Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -402,7 +408,7 @@
      * implementation.
      * @hide
      */
-    int TRANSIT_FIRST_CUSTOM = 10;
+    int TRANSIT_FIRST_CUSTOM = 11;
 
     /**
      * @hide
@@ -418,6 +424,7 @@
             TRANSIT_KEYGUARD_GOING_AWAY,
             TRANSIT_KEYGUARD_OCCLUDE,
             TRANSIT_KEYGUARD_UNOCCLUDE,
+            TRANSIT_PIP,
             TRANSIT_FIRST_CUSTOM
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -467,6 +474,13 @@
     int TRANSIT_FLAG_KEYGUARD_LOCKED = 0x40;
 
     /**
+     * Transition flag: Indicates that this transition is for recents animation.
+     * TODO(b/188669821): Remove once special-case logic moves to shell.
+     * @hide
+     */
+    int TRANSIT_FLAG_IS_RECENTS = 0x80;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -476,7 +490,8 @@
             TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
             TRANSIT_FLAG_APP_CRASHED,
             TRANSIT_FLAG_OPEN_BEHIND,
-            TRANSIT_FLAG_KEYGUARD_LOCKED
+            TRANSIT_FLAG_KEYGUARD_LOCKED,
+            TRANSIT_FLAG_IS_RECENTS
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
@@ -3468,6 +3483,22 @@
         public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
 
         /**
+         * If specified, the insets provided by this window will be our window frame minus the
+         * insets specified by providedInternalInsets.
+         *
+         * @hide
+         */
+        public Insets providedInternalInsets = Insets.NONE;
+
+        /**
+         * {@link LayoutParams} to be applied to the window when layout with a assigned rotation.
+         * This will make layout during rotation change smoothly.
+         *
+         * @hide
+         */
+        public LayoutParams[] paramsForRotation;
+
+        /**
          * Specifies types of insets that this window should avoid overlapping during layout.
          *
          * @param types which {@link WindowInsets.Type}s of insets that this window should avoid.
@@ -3566,6 +3597,18 @@
             return mFitInsetsIgnoringVisibility;
         }
 
+        private void checkNonRecursiveParams() {
+            if (paramsForRotation == null) {
+                return;
+            }
+            for (int i = paramsForRotation.length - 1; i >= 0; i--) {
+                if (paramsForRotation[i].paramsForRotation != null) {
+                    throw new IllegalArgumentException(
+                            "Params cannot contain params recursively.");
+                }
+            }
+        }
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -3820,6 +3863,14 @@
             } else {
                 out.writeInt(0);
             }
+            providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */);
+            if (paramsForRotation != null) {
+                checkNonRecursiveParams();
+                out.writeInt(paramsForRotation.length);
+                out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
+            } else {
+                out.writeInt(0);
+            }
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3891,6 +3942,12 @@
                 providesInsetsTypes = new int[insetsTypesLength];
                 in.readIntArray(providesInsetsTypes);
             }
+            providedInternalInsets = Insets.CREATOR.createFromParcel(in);
+            int paramsForRotationLength = in.readInt();
+            if (paramsForRotationLength > 0) {
+                paramsForRotation = new LayoutParams[paramsForRotationLength];
+                in.readTypedArray(paramsForRotation, LayoutParams.CREATOR);
+            }
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -4187,6 +4244,17 @@
                 changes |= LAYOUT_CHANGED;
             }
 
+            if (!providedInternalInsets.equals(o.providedInternalInsets)) {
+                providedInternalInsets = o.providedInternalInsets;
+                changes |= LAYOUT_CHANGED;
+            }
+
+            if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
+                paramsForRotation = o.paramsForRotation;
+                checkNonRecursiveParams();
+                changes |= LAYOUT_CHANGED;
+            }
+
             return changes;
         }
 
@@ -4382,6 +4450,18 @@
                     sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
                 }
             }
+            if (!providedInternalInsets.equals(Insets.NONE)) {
+                sb.append(" providedInternalInsets=");
+                sb.append(providedInternalInsets);
+            }
+            if (paramsForRotation != null && paramsForRotation.length != 0) {
+                sb.append(System.lineSeparator());
+                sb.append(prefix).append("  paramsForRotation=");
+                for (int i = 0; i < paramsForRotation.length; ++i) {
+                    if (i > 0) sb.append(' ');
+                    sb.append(paramsForRotation[i].toString());
+                }
+            }
 
             sb.append('}');
             return sb.toString();
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f6d6fde..40da86a 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1298,6 +1298,7 @@
         parcel.readList(record.mText, null);
         record.mSourceWindowId = parcel.readInt();
         record.mSourceNodeId = parcel.readLong();
+        record.mSourceDisplayId = parcel.readInt();
         record.mSealed = (parcel.readInt() == 1);
     }
 
@@ -1364,6 +1365,7 @@
         parcel.writeList(record.mText);
         parcel.writeInt(record.mSourceWindowId);
         parcel.writeLong(record.mSourceNodeId);
+        parcel.writeInt(record.mSourceDisplayId);
         parcel.writeInt(record.mSealed ? 1 : 0);
     }
 
@@ -1402,6 +1404,7 @@
             if (DEBUG) {
                 builder.append("; SourceWindowId: 0x").append(Long.toHexString(mSourceWindowId));
                 builder.append("; SourceNodeId: 0x").append(Long.toHexString(mSourceNodeId));
+                builder.append("; SourceDisplayId: ").append(mSourceDisplayId);
             }
             for (int i = 0; i < getRecordCount(); i++) {
                 builder.append("  Record ").append(i).append(":");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index dd81dd9..80ef6cf 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -16,6 +16,9 @@
 
 package android.view.accessibility;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -86,6 +89,7 @@
     public static final int NO_ID = -1;
 
     public static final String CALL_STACK = "call_stack";
+    public static final String IGNORE_CALL_STACK = "ignore_call_stack";
 
     private static final String LOG_TAG = "AccessibilityInteractionClient";
 
@@ -121,6 +125,12 @@
 
     private volatile int mInteractionId = -1;
     private volatile int mCallingUid = Process.INVALID_UID;
+    // call stack for IAccessibilityInteractionConnectionCallback APIs. These callback APIs are
+    // shared by multiple requests APIs in IAccessibilityServiceConnection. To correctly log the
+    // request API which triggers the callback, we log trace entries for callback after the
+    // request API thread waiting for the callback returns. To log the correct callback stack in
+    // the request API thread, we save the callback stack in this member variables.
+    private List<StackTraceElement> mCallStackOfCallback;
 
     private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
 
@@ -307,18 +317,30 @@
                         if (DEBUG) {
                             Log.i(LOG_TAG, "Window cache hit");
                         }
+                        if (shouldTraceClient()) {
+                            logTraceClient(connection, "getWindow cache",
+                                    "connectionId=" + connectionId + ";accessibilityWindowId="
+                                    + accessibilityWindowId + ";bypassCache=false");
+                        }
                         return window;
                     }
                     if (DEBUG) {
                         Log.i(LOG_TAG, "Window cache miss");
                     }
                 }
+
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
                     window = connection.getWindow(accessibilityWindowId);
                 } finally {
                     Binder.restoreCallingIdentity(identityToken);
                 }
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "getWindow", "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId + ";bypassCache="
+                            + bypassCache);
+                }
+
                 if (window != null) {
                     if (!bypassCache) {
                         sAccessibilityCache.addWindow(window);
@@ -368,6 +390,10 @@
                     if (DEBUG) {
                         Log.i(LOG_TAG, "Windows cache hit");
                     }
+                    if (shouldTraceClient()) {
+                        logTraceClient(
+                                connection, "getWindows cache", "connectionId=" + connectionId);
+                    }
                     return windows;
                 }
                 if (DEBUG) {
@@ -379,6 +405,9 @@
                 } finally {
                     Binder.restoreCallingIdentity(identityToken);
                 }
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
+                }
                 if (windows != null) {
                     sAccessibilityCache.setWindowsOnAllDisplays(windows);
                     return windows;
@@ -472,6 +501,15 @@
                             Log.i(LOG_TAG, "Node cache hit for "
                                     + idToString(accessibilityWindowId, accessibilityNodeId));
                         }
+                        if (shouldTraceClient()) {
+                            logTraceClient(connection,
+                                    "findAccessibilityNodeInfoByAccessibilityId cache",
+                                    "connectionId=" + connectionId + ";accessibilityWindowId="
+                                    + accessibilityWindowId + ";accessibilityNodeId="
+                                    + accessibilityNodeId + ";bypassCache=" + bypassCache
+                                    + ";prefetchFlags=" + prefetchFlags + ";arguments="
+                                    + arguments);
+                        }
                         return cachedInfo;
                     }
                     if (DEBUG) {
@@ -488,6 +526,14 @@
                     prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
                 }
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "findAccessibilityNodeInfoByAccessibilityId",
+                            "InteractionId:" + interactionId + "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId
+                            + ";accessibilityNodeId=" + accessibilityNodeId + ";bypassCache="
+                            + bypassCache + ";prefetchFlags=" + prefetchFlags + ";arguments="
+                            + arguments);
+                }
                 final String[] packageNames;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
@@ -500,16 +546,10 @@
                 if (packageNames != null) {
                     AccessibilityNodeInfo info =
                             getFindAccessibilityNodeInfoResultAndClear(interactionId);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId",
-                                "InteractionId:" + interactionId + ";Result: " + info
-                                        + ";connectionId=" + connectionId
-                                        + ";accessibilityWindowId="
-                                        + accessibilityWindowId + ";accessibilityNodeId="
-                                        + accessibilityNodeId + ";bypassCache=" + bypassCache
-                                        + ";prefetchFlags=" + prefetchFlags
-                                        + ";arguments=" + arguments);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "findAccessibilityNodeInfoByAccessibilityId",
+                                "InteractionId:" + interactionId + ";connectionId="
+                                + connectionId + ";Result: " + info);
                     }
                     if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
                             && info != null) {
@@ -571,6 +611,14 @@
                 final String[] packageNames;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
+                    if (shouldTraceClient()) {
+                        logTraceClient(connection, "findAccessibilityNodeInfosByViewId",
+                                "InteractionId=" + interactionId + ";connectionId=" + connectionId
+                                + ";accessibilityWindowId=" + accessibilityWindowId
+                                + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
+                                + viewId);
+                    }
+
                     packageNames = connection.findAccessibilityNodeInfosByViewId(
                             accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
                             Thread.currentThread().getId());
@@ -581,13 +629,10 @@
                 if (packageNames != null) {
                     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
                             interactionId);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId="
-                                + interactionId + ":Result: " + infos + ";connectionId="
-                                + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
-                                + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
-                                + viewId);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "findAccessibilityNodeInfosByViewId",
+                                "InteractionId=" + interactionId + ";connectionId=" + connectionId
+                                + ":Result: " + infos);
                     }
                     if (infos != null) {
                         finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -630,6 +675,12 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "findAccessibilityNodeInfosByText",
+                            "InteractionId:" + interactionId + "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId
+                            + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+                }
                 final String[] packageNames;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
@@ -643,12 +694,10 @@
                 if (packageNames != null) {
                     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
                             interactionId);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId="
-                                + interactionId + ":Result: " + infos + ";connectionId="
-                                + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
-                                + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "findAccessibilityNodeInfosByText",
+                                "InteractionId=" + interactionId + ";connectionId=" + connectionId
+                                + ";Result: " + infos);
                     }
                     if (infos != null) {
                         finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -690,6 +739,13 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "findFocus",
+                            "InteractionId:" + interactionId + "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId
+                            + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
+                            + focusType);
+                }
                 final String[] packageNames;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
@@ -703,13 +759,9 @@
                 if (packageNames != null) {
                     AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
                             interactionId);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "findFocus", "InteractionId=" + interactionId
-                                + ":Result: " + info + ";connectionId=" + connectionId
-                                + ";accessibilityWindowId=" + accessibilityWindowId
-                                + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
-                                + focusType);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "findFocus", "InteractionId=" + interactionId
+                                + ";connectionId=" + connectionId + ";Result:" + info);
                     }
                     finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
                     return info;
@@ -747,6 +799,13 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "focusSearch",
+                            "InteractionId:" + interactionId + "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId
+                            + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
+                            + direction);
+                }
                 final String[] packageNames;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
@@ -761,13 +820,9 @@
                     AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
                             interactionId);
                     finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "focusSearch", "InteractionId=" + interactionId
-                                + ":Result: " + info + ";connectionId=" + connectionId
-                                + ";accessibilityWindowId=" + accessibilityWindowId
-                                + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
-                                + direction);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "focusSearch", "InteractionId=" + interactionId
+                                + ";connectionId=" + connectionId + ";Result:" + info);
                     }
                     return info;
                 }
@@ -803,6 +858,13 @@
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
+                if (shouldTraceClient()) {
+                    logTraceClient(connection, "performAccessibilityAction",
+                            "InteractionId:" + interactionId + "connectionId=" + connectionId
+                            + ";accessibilityWindowId=" + accessibilityWindowId
+                            + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + action
+                            + ";arguments=" + arguments);
+                }
                 final boolean success;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
@@ -816,13 +878,10 @@
                 if (success) {
                     final boolean result =
                             getPerformAccessibilityActionResultAndClear(interactionId);
-                    if (mAccessibilityManager != null
-                            && mAccessibilityManager.isAccessibilityTracingEnabled()) {
-                        logTrace(connection, "performAccessibilityAction", "InteractionId="
-                                + interactionId + ":Result: " + result + ";connectionId="
-                                + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
-                                + ";accessibilityNodeId=" + accessibilityNodeId + ";action="
-                                + action + ";arguments=" + arguments);
+                    if (shouldTraceCallback()) {
+                        logTraceCallback(connection, "performAccessibilityAction",
+                                "InteractionId=" + interactionId + ";connectionId=" + connectionId
+                                + ";Result: " + result);
                     }
                     return result;
                 }
@@ -886,6 +945,8 @@
                 mFindAccessibilityNodeInfoResult = info;
                 mInteractionId = interactionId;
                 mCallingUid = Binder.getCallingUid();
+                mCallStackOfCallback = new ArrayList<StackTraceElement>(
+                        Arrays.asList(Thread.currentThread().getStackTrace()));
             }
             mInstanceLock.notifyAll();
         }
@@ -936,6 +997,8 @@
                 }
                 mInteractionId = interactionId;
                 mCallingUid = Binder.getCallingUid();
+                mCallStackOfCallback = new ArrayList<StackTraceElement>(
+                    Arrays.asList(Thread.currentThread().getStackTrace()));
             }
             mInstanceLock.notifyAll();
         }
@@ -975,13 +1038,15 @@
             finalizeAndCacheAccessibilityNodeInfos(
                     infos, connectionIdWaitingForPrefetchResultCopy, false,
                     packageNamesForNextPrefetchResultCopy);
-            if (mAccessibilityManager != null
-                    && mAccessibilityManager.isAccessibilityTracingEnabled()) {
+            if (shouldTraceCallback()) {
                 logTrace(getConnection(connectionIdWaitingForPrefetchResultCopy),
                         "setPrefetchAccessibilityNodeInfoResult",
-                        "InteractionId:" + interactionId + ";Result: " + infos
-                                + ";connectionId=" + connectionIdWaitingForPrefetchResultCopy,
-                        Binder.getCallingUid());
+                        "InteractionId:" + interactionId + ";connectionId="
+                        + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos,
+                        Binder.getCallingUid(),
+                        Arrays.asList(Thread.currentThread().getStackTrace()),
+                        new HashSet<String>(Arrays.asList("getStackTrace")),
+                        FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
             }
         } else if (DEBUG) {
             Log.w(LOG_TAG, "Prefetching for interaction with id " + interactionId + " dropped "
@@ -1013,6 +1078,8 @@
                 mPerformAccessibilityActionResult = succeeded;
                 mInteractionId = interactionId;
                 mCallingUid = Binder.getCallingUid();
+                mCallStackOfCallback = new ArrayList<StackTraceElement>(
+                    Arrays.asList(Thread.currentThread().getStackTrace()));
             }
             mInstanceLock.notifyAll();
         }
@@ -1222,24 +1289,47 @@
         return true;
     }
 
+    private boolean shouldTraceClient() {
+        return (mAccessibilityManager != null)
+                && mAccessibilityManager.isA11yInteractionClientTraceEnabled();
+    }
+
+    private boolean shouldTraceCallback() {
+        return (mAccessibilityManager != null)
+                && mAccessibilityManager.isA11yInteractionConnectionCBTraceEnabled();
+    }
+
     private void logTrace(
             IAccessibilityServiceConnection connection, String method, String params,
-            int callingUid) {
+            int callingUid, List<StackTraceElement> callStack, HashSet<String> ignoreSet,
+            long logTypes) {
         try {
             Bundle b = new Bundle();
-            ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>(
-                    Arrays.asList(Thread.currentThread().getStackTrace()));
-            b.putSerializable(CALL_STACK, callStack);
+            b.putSerializable(CALL_STACK, new ArrayList<StackTraceElement>(callStack));
+            if (ignoreSet != null) {
+                b.putSerializable(IGNORE_CALL_STACK, ignoreSet);
+            }
             connection.logTrace(SystemClock.elapsedRealtimeNanos(),
-                    LOG_TAG + ".callback for " + method, params, Process.myPid(),
-                    Thread.currentThread().getId(), callingUid, b);
+                    LOG_TAG + "." + method,
+                    logTypes, params, Process.myPid(), Thread.currentThread().getId(),
+                    callingUid, b);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Failed to log trace. " + e);
         }
     }
 
-    private void logTrace(
+    private void logTraceCallback(
             IAccessibilityServiceConnection connection, String method, String params) {
-        logTrace(connection, method, params, mCallingUid);
+        logTrace(connection, method + " callback", params, mCallingUid, mCallStackOfCallback,
+                new HashSet<String>(Arrays.asList("getStackTrace")),
+                FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
+    }
+
+    private void logTraceClient(
+            IAccessibilityServiceConnection connection, String method, String params) {
+        logTrace(connection, method, params, Binder.getCallingUid(),
+                Arrays.asList(Thread.currentThread().getStackTrace()),
+                new HashSet<String>(Arrays.asList("getStackTrace", "logTraceClient")),
+                FLAGS_ACCESSIBILITY_INTERACTION_CLIENT);
     }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index f9cdbd3..17fad7e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -111,7 +111,13 @@
     public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010;
 
     /** @hide */
-    public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020;
+    public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100;
+    /** @hide */
+    public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200;
+    /** @hide */
+    public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400;
+    /** @hide */
+    public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800;
 
     /** @hide */
     public static final int DALTONIZER_DISABLED = -1;
@@ -235,8 +241,8 @@
     @UnsupportedAppUsage(trackingBug = 123768939L)
     boolean mIsHighTextContrastEnabled;
 
-    // Whether accessibility tracing is enabled or not
-    boolean mIsAccessibilityTracingEnabled = false;
+    // accessibility tracing state
+    int mAccessibilityTracingState = 0;
 
     AccessibilityPolicy mAccessibilityPolicy;
 
@@ -1029,13 +1035,50 @@
     }
 
     /**
-     * Gets accessibility tracing enabled state.
+     * Gets accessibility interaction connection tracing enabled state.
      *
      * @hide
      */
-    public boolean isAccessibilityTracingEnabled() {
+    public boolean isA11yInteractionConnectionTraceEnabled() {
         synchronized (mLock) {
-            return mIsAccessibilityTracingEnabled;
+            return ((mAccessibilityTracingState
+                    & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0);
+        }
+    }
+
+    /**
+     * Gets accessibility interaction connection callback tracing enabled state.
+     *
+     * @hide
+     */
+    public boolean isA11yInteractionConnectionCBTraceEnabled() {
+        synchronized (mLock) {
+            return ((mAccessibilityTracingState
+                    & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0);
+        }
+    }
+
+    /**
+     * Gets accessibility interaction client tracing enabled state.
+     *
+     * @hide
+     */
+    public boolean isA11yInteractionClientTraceEnabled() {
+        synchronized (mLock) {
+            return ((mAccessibilityTracingState
+                    & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0);
+        }
+    }
+
+    /**
+     * Gets accessibility service tracing enabled state.
+     *
+     * @hide
+     */
+    public boolean isA11yServiceTraceEnabled() {
+        synchronized (mLock) {
+            return ((mAccessibilityTracingState
+                    & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0);
         }
     }
 
@@ -1233,8 +1276,6 @@
                 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
         final boolean highTextContrastEnabled =
                 (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
-        final boolean accessibilityTracingEnabled =
-                (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0;
 
         final boolean wasEnabled = isEnabled();
         final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
@@ -1257,7 +1298,7 @@
             notifyHighTextContrastStateChanged();
         }
 
-        updateAccessibilityTracingState(accessibilityTracingEnabled);
+        updateAccessibilityTracingState(stateFlags);
     }
 
     /**
@@ -1715,11 +1756,11 @@
     }
 
     /**
-     * Update mIsAccessibilityTracingEnabled.
+     * Update mAccessibilityTracingState.
      */
-    private void updateAccessibilityTracingState(boolean enabled) {
+    private void updateAccessibilityTracingState(int stateFlag) {
         synchronized (mLock) {
-            mIsAccessibilityTracingEnabled = enabled;
+            mAccessibilityTracingState = stateFlag;
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index c3a4d32..f26abb2 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -20,8 +20,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcelable;
+import android.view.Display;
 import android.view.View;
 
 import java.util.ArrayList;
@@ -104,6 +106,7 @@
     @UnsupportedAppUsage
     long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
     int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+    int mSourceDisplayId = Display.INVALID_DISPLAY;
 
     CharSequence mClassName;
     CharSequence mContentDescription;
@@ -202,6 +205,27 @@
     }
 
     /**
+     * Sets the display id.
+     *
+     * @param displayId The displayId id.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setDisplayId(int displayId) {
+        mSourceDisplayId = displayId;
+    }
+
+    /**
+     * Gets the id of the display from which the event comes from.
+     *
+     * @return The display id.
+     */
+    public int getDisplayId() {
+        return mSourceDisplayId;
+    }
+
+    /**
      * Sets the window id.
      *
      * @param windowId The window id.
@@ -886,6 +910,7 @@
         mText.addAll(record.mText);
         mSourceWindowId = record.mSourceWindowId;
         mSourceNodeId = record.mSourceNodeId;
+        mSourceDisplayId = record.mSourceDisplayId;
         mConnectionId = record.mConnectionId;
     }
 
@@ -914,6 +939,7 @@
         mText.clear();
         mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
         mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+        mSourceDisplayId = Display.INVALID_DISPLAY;
         mConnectionId = UNDEFINED;
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index edcb59a..76e2261 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Parcel;
@@ -114,6 +115,7 @@
     private int mBooleanProperties;
     private int mId = UNDEFINED_WINDOW_ID;
     private int mParentId = UNDEFINED_WINDOW_ID;
+    private int mTaskId = ActivityTaskManager.INVALID_TASK_ID;
     private Region mRegionInScreen = new Region();
     private LongArray mChildIds;
     private CharSequence mTitle;
@@ -307,6 +309,28 @@
     }
 
     /**
+     * Gets the task ID.
+     *
+     * @return The task ID.
+     *
+     * @hide
+     */
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Sets the task ID.
+     *
+     * @param taskId The task ID.
+     *
+     * @hide
+     */
+    public void setTaskId(int taskId) {
+        mTaskId = taskId;
+    }
+
+    /**
      * Sets the unique id of the IAccessibilityServiceConnection over which
      * this instance can send requests to the system.
      *
@@ -578,6 +602,7 @@
         parcel.writeInt(mBooleanProperties);
         parcel.writeInt(mId);
         parcel.writeInt(mParentId);
+        parcel.writeInt(mTaskId);
         mRegionInScreen.writeToParcel(parcel, flags);
         parcel.writeCharSequence(mTitle);
         parcel.writeLong(mAnchorId);
@@ -608,6 +633,7 @@
         mBooleanProperties = other.mBooleanProperties;
         mId = other.mId;
         mParentId = other.mParentId;
+        mTaskId = other.mTaskId;
         mRegionInScreen.set(other.mRegionInScreen);
         mTitle = other.mTitle;
         mAnchorId = other.mAnchorId;
@@ -631,6 +657,7 @@
         mBooleanProperties = parcel.readInt();
         mId = parcel.readInt();
         mParentId = parcel.readInt();
+        mTaskId = parcel.readInt();
         mRegionInScreen = Region.CREATOR.createFromParcel(parcel);
         mTitle = parcel.readCharSequence();
         mAnchorId = parcel.readLong();
@@ -676,6 +703,7 @@
         builder.append("title=").append(mTitle);
         builder.append(", displayId=").append(mDisplayId);
         builder.append(", id=").append(mId);
+        builder.append(", taskId=").append(mTaskId);
         builder.append(", type=").append(typeToString(mType));
         builder.append(", layer=").append(mLayer);
         builder.append(", region=").append(mRegionInScreen);
@@ -719,6 +747,7 @@
         mBooleanProperties = 0;
         mId = UNDEFINED_WINDOW_ID;
         mParentId = UNDEFINED_WINDOW_ID;
+        mTaskId = ActivityTaskManager.INVALID_TASK_ID;
         mRegionInScreen.setEmpty();
         mChildIds = null;
         mConnectionId = UNDEFINED_WINDOW_ID;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d065147..1122056 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,6 +25,7 @@
 import static android.view.autofill.Helper.toList;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -45,16 +46,21 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
 import android.service.autofill.FillEventHistory;
+import android.service.autofill.IFillCallback;
 import android.service.autofill.UserData;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -74,6 +80,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -83,7 +90,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.SyncResultReceiver;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -99,6 +105,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import sun.misc.Cleaner;
 
@@ -167,6 +174,12 @@
  * shows an autofill save UI if the value of savable views have changed. If the user selects the
  * option to Save, the current value of the views is then sent to the autofill service.
  *
+ * <p>There is another choice for the application to provide it's datasets to the Autofill framework
+ * by setting an {@link AutofillRequestCallback} through
+ * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
+ * its callback instead of the default {@link AutofillService}. See
+ * {@link AutofillRequestCallback} for more details.
+ *
  * <h3 id="additional-notes">Additional notes</h3>
  *
  * <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -292,6 +305,7 @@
     /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
     /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
+    /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
 
     // NOTE: flag below is used by the session start receiver only, hence it can have values above
     /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -592,6 +606,11 @@
     @GuardedBy("mLock")
     private boolean mEnabledForAugmentedAutofillOnly;
 
+    @GuardedBy("mLock")
+    @Nullable private AutofillRequestCallback mAutofillRequestCallback;
+    @GuardedBy("mLock")
+    @Nullable private Executor mRequestCallbackExecutor;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -726,7 +745,7 @@
      * @hide
      */
     public AutofillManager(Context context, IAutoFillManager service) {
-        mContext = Preconditions.checkNotNull(context, "context cannot be null");
+        mContext = Objects.requireNonNull(context, "context cannot be null");
         mService = service;
         mOptions = context.getAutofillOptions();
 
@@ -1836,6 +1855,32 @@
         return new AutofillId(parent.getAutofillViewId(), virtualId);
     }
 
+    /**
+     * Sets the client's suggestions callback for autofill.
+     *
+     * @see AutofillRequestCallback
+     *
+     * @param executor specifies the thread upon which the callbacks will be invoked.
+     * @param callback which handles autofill request to provide client's suggestions.
+     */
+    public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AutofillRequestCallback callback) {
+        synchronized (mLock) {
+            mRequestCallbackExecutor = executor;
+            mAutofillRequestCallback = callback;
+        }
+    }
+
+    /**
+     * clears the client's suggestions callback for autofill.
+     */
+    public void clearAutofillRequestCallback() {
+        synchronized (mLock) {
+            mRequestCallbackExecutor = null;
+            mAutofillRequestCallback = null;
+        }
+    }
+
     @GuardedBy("mLock")
     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
             @NonNull AutofillValue value, int flags) {
@@ -1896,6 +1941,13 @@
                 }
             }
 
+            if (mAutofillRequestCallback != null) {
+                if (sDebug) {
+                    Log.d(TAG, "startSession with the client suggestions provider");
+                }
+                flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
+            }
+
             mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, clientActivity,
@@ -2245,6 +2297,28 @@
         }
     }
 
+    private void onFillRequest(InlineSuggestionsRequest request,
+            CancellationSignal cancellationSignal, FillCallback callback) {
+        final AutofillRequestCallback autofillRequestCallback;
+        final Executor executor;
+        synchronized (mLock) {
+            autofillRequestCallback = mAutofillRequestCallback;
+            executor = mRequestCallbackExecutor;
+        }
+        if (autofillRequestCallback != null && executor != null) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() ->
+                        autofillRequestCallback.onFillRequest(
+                                request, cancellationSignal, callback));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        } else {
+            callback.onSuccess(null);
+        }
+    }
+
     /** @hide */
     public static final int SET_STATE_FLAG_ENABLED = 0x01;
     /** @hide */
@@ -3627,6 +3701,23 @@
                 afm.post(() -> afm.requestShowSoftInput(id));
             }
         }
+
+        @Override
+        public void requestFillFromClient(int id, InlineSuggestionsRequest request,
+                IFillCallback callback) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                ICancellationSignal transport = CancellationSignal.createTransport();
+                try {
+                    callback.onCancellable(transport);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error requesting a cancellation", e);
+                }
+
+                afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
+                        new FillCallback(callback, id));
+            }
+        }
     }
 
     private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
new file mode 100644
index 0000000..e632a58
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillRequestCallback.java
@@ -0,0 +1,72 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.service.autofill.FillCallback;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * <p>This class is used to provide some input suggestions to the Autofill framework.
+ *
+ * <P>When the user is requested to input something, Autofill will try to query input suggestions
+ * for the user choosing. If the application want to provide some internal input suggestions,
+ * implements this callback and register via
+ * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
+ * AutofillRequestCallback)}. Autofill will callback the
+ * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
+ * input suggestions.
+ *
+ * <P>To make sure the callback to take effect, must register before the autofill session starts.
+ * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
+ * session, and then the callback will be used at the next restarted session.
+ *
+ * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
+ * {@link AutofillId}s from its view structure. Below is an example:
+ * <pre class="prettyprint">
+ * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
+ * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
+ * </pre>
+ * To learn more about creating a {@link android.service.autofill.FillResponse}, read
+ * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
+ *
+ * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
+ * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
+ * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
+ * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
+ * client would like to keep no suggestions for the field, respond with an empty
+ * {@link android.service.autofill.FillResponse} which has no dataset.
+ *
+ * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
+ * the keyboard may choose to block your app from the inline strip.
+ */
+public interface AutofillRequestCallback {
+    /**
+     * Called by the Android system to decide if a screen can be autofilled by the callback.
+     *
+     * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
+     *     currently inline suggestions are supported and can be displayed.
+     * @param cancellationSignal signal for observing cancellation requests. The system will use
+     *     this to notify you that the fill result is no longer needed and you should stop
+     *     handling this fill request in order to save resources.
+     * @param callback object used to notify the result of the request.
+     */
+    void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 1f833f6..64507aa 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,9 +24,11 @@
 import android.content.IntentSender;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.service.autofill.IFillCallback;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.KeyEvent;
 
 import com.android.internal.os.IResultReceiver;
@@ -140,4 +142,10 @@
     * Requests to show the soft input method if the focus is on the given id.
     */
    void requestShowSoftInput(in AutofillId id);
+
+    /**
+     * Requests to determine if a screen can be autofilled by the client app.
+     */
+    void requestFillFromClient(int id, in InlineSuggestionsRequest request,
+            in IFillCallback callback);
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 1adef94..027c8d2 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -23,10 +23,9 @@
 import android.os.Parcelable;
 import android.util.DebugUtils;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Defines a condition for when content capture should be allowed.
@@ -61,7 +60,7 @@
      * the {@code LocusId} used in the {@link ContentCaptureContext}).
      */
     public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) {
-        this.mLocusId = Preconditions.checkNotNull(locusId);
+        this.mLocusId = Objects.requireNonNull(locusId);
         this.mFlags = flags;
     }
 
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9998fbc..71b8003 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -276,7 +276,7 @@
          * @param id id associated with this context.
          */
         public Builder(@NonNull LocusId id) {
-            mId = Preconditions.checkNotNull(id);
+            mId = Objects.requireNonNull(id);
         }
 
         /**
@@ -291,7 +291,7 @@
          */
         @NonNull
         public Builder setExtras(@NonNull Bundle extras) {
-            mExtras = Preconditions.checkNotNull(extras);
+            mExtras =  Objects.requireNonNull(extras);
             throwIfDestroyed();
             return this;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ce6d034..ae45c6e 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -33,13 +33,12 @@
 import android.view.autofill.AutofillId;
 import android.view.inputmethod.BaseInputConnection;
 
-import com.android.internal.util.Preconditions;
-
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /** @hide */
 @SystemApi
@@ -173,13 +172,13 @@
 
     /** @hide */
     public ContentCaptureEvent setAutofillId(@NonNull AutofillId id) {
-        mId = Preconditions.checkNotNull(id);
+        mId = Objects.requireNonNull(id);
         return this;
     }
 
     /** @hide */
     public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
-        mIds = Preconditions.checkNotNull(ids);
+        mIds = Objects.requireNonNull(ids);
         return this;
     }
 
@@ -189,7 +188,7 @@
      * @hide
      */
     public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) {
-        Preconditions.checkNotNull(id);
+        Objects.requireNonNull(id);
         if (mIds == null) {
             mIds = new ArrayList<>();
             if (mId == null) {
@@ -253,7 +252,7 @@
     /** @hide */
     @NonNull
     public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
-        mNode = Preconditions.checkNotNull(node);
+        mNode = Objects.requireNonNull(node);
         return this;
     }
 
@@ -425,7 +424,7 @@
      * @hide
      */
     public void mergeEvent(@NonNull ContentCaptureEvent event) {
-        Preconditions.checkNotNull(event);
+        Objects.requireNonNull(event);
         final int eventType = event.getType();
         if (mType != eventType) {
             Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType) + ") cannot be merged "
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9241c30..8514f6f 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -49,7 +49,6 @@
 import android.view.contentcapture.ContentCaptureSession.FlushReason;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
@@ -59,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -398,9 +398,9 @@
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
             @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) {
-        mContext = Preconditions.checkNotNull(context, "context cannot be null");
-        mService = Preconditions.checkNotNull(service, "service cannot be null");
-        mOptions = Preconditions.checkNotNull(options, "options cannot be null");
+        mContext = Objects.requireNonNull(context, "context cannot be null");
+        mService = Objects.requireNonNull(service, "service cannot be null");
+        mOptions = Objects.requireNonNull(options, "options cannot be null");
 
         ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel);
 
@@ -679,7 +679,7 @@
      * @param request object specifying what user data should be removed.
      */
     public void removeData(@NonNull DataRemovalRequest request) {
-        Preconditions.checkNotNull(request);
+        Objects.requireNonNull(request);
 
         try {
             mService.removeData(request);
@@ -703,9 +703,9 @@
     public void shareData(@NonNull DataShareRequest request,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull DataShareWriteAdapter dataShareWriteAdapter) {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(dataShareWriteAdapter);
-        Preconditions.checkNotNull(executor);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(dataShareWriteAdapter);
+        Objects.requireNonNull(executor);
 
         try {
             mService.shareData(request,
@@ -840,9 +840,9 @@
 
         private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter,
                 LocalDataShareAdapterResourceManager resourceManager) {
-            Preconditions.checkNotNull(executor);
-            Preconditions.checkNotNull(adapter);
-            Preconditions.checkNotNull(resourceManager);
+            Objects.requireNonNull(executor);
+            Objects.requireNonNull(adapter);
+            Objects.requireNonNull(resourceManager);
 
             resourceManager.initializeForDelegate(this, adapter, executor);
             mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index cc47f09..9552691 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.security.SecureRandom;
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Session used when notifying the Android system about events associated with views.
@@ -231,7 +232,7 @@
     // Used by ChildCOntentCaptureSession
     ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
         this();
-        mClientContext = Preconditions.checkNotNull(initialContext);
+        mClientContext = Objects.requireNonNull(initialContext);
     }
 
     /** @hide */
@@ -362,7 +363,7 @@
      * @param node node that has been added.
      */
     public final void notifyViewAppeared(@NonNull ViewStructure node) {
-        Preconditions.checkNotNull(node);
+        Objects.requireNonNull(node);
         if (!isContentCaptureEnabled()) return;
 
         if (!(node instanceof ViewNode.ViewStructureImpl)) {
@@ -383,7 +384,7 @@
      * @param id id of the node that has been removed.
      */
     public final void notifyViewDisappeared(@NonNull AutofillId id) {
-        Preconditions.checkNotNull(id);
+        Objects.requireNonNull(id);
         if (!isContentCaptureEnabled()) return;
 
         internalNotifyViewDisappeared(id);
@@ -424,7 +425,7 @@
      * @param text new text.
      */
     public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
-        Preconditions.checkNotNull(id);
+        Objects.requireNonNull(id);
 
         if (!isContentCaptureEnabled()) return;
 
@@ -438,7 +439,7 @@
      * Notifies the Intelligence Service that the insets of a view have changed.
      */
     public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) {
-        Preconditions.checkNotNull(viewInsets);
+        Objects.requireNonNull(viewInsets);
 
         if (!isContentCaptureEnabled()) return;
 
@@ -534,7 +535,7 @@
      * @throws IllegalArgumentException if the {@code parentId} is a virtual child id.
      */
     public @NonNull AutofillId newAutofillId(@NonNull AutofillId hostId, long virtualChildId) {
-        Preconditions.checkNotNull(hostId);
+        Objects.requireNonNull(hostId);
         Preconditions.checkArgument(hostId.isNonVirtual(), "hostId cannot be virtual: %s", hostId);
         return new AutofillId(hostId, virtualChildId, mId);
     }
diff --git a/core/java/android/view/contentcapture/DataRemovalRequest.java b/core/java/android/view/contentcapture/DataRemovalRequest.java
index f403dac..3d86113 100644
--- a/core/java/android/view/contentcapture/DataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/DataRemovalRequest.java
@@ -29,6 +29,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Class used by apps to request the content capture service to remove data associated with
@@ -143,7 +144,7 @@
         public Builder addLocusId(@NonNull LocusId locusId, @Flags int flags) {
             throwIfDestroyed();
             Preconditions.checkState(!mForEverything, "Already is for everything");
-            Preconditions.checkNotNull(locusId);
+            Objects.requireNonNull(locusId);
             // felipeal: check flags
 
             if (mLocusIds == null) {
diff --git a/core/java/android/view/contentcapture/DataShareRequest.java b/core/java/android/view/contentcapture/DataShareRequest.java
index 78c0ef9..b2eabf1 100644
--- a/core/java/android/view/contentcapture/DataShareRequest.java
+++ b/core/java/android/view/contentcapture/DataShareRequest.java
@@ -24,7 +24,8 @@
 import android.os.Parcelable;
 
 import com.android.internal.util.DataClass;
-import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
 
 /** Container class representing a request to share data with Content Capture service. */
 @DataClass(
@@ -47,7 +48,7 @@
 
     /** Constructs a request to share data with the Content Capture Service. */
     public DataShareRequest(@Nullable LocusId locusId, @NonNull String mimeType) {
-        Preconditions.checkNotNull(mimeType);
+        Objects.requireNonNull(mimeType);
 
         mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
         mLocusId = locusId;
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index c882c6e..1b4a00f 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -34,7 +34,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 //TODO(b/122484602): add javadocs / implement Parcelable / implement
 //TODO(b/122484602): for now it's extending ViewNode directly as it needs most of its properties,
@@ -659,7 +659,7 @@
         /** @hide */
         @TestApi
         public ViewStructureImpl(@NonNull View view) {
-            mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId();
+            mNode.mAutofillId = Objects.requireNonNull(view).getAutofillId();
             final ViewParent parent = view.getParent();
             if (parent instanceof View) {
                 mNode.mParentAutofillId = ((View) parent).getAutofillId();
@@ -669,7 +669,7 @@
         /** @hide */
         @TestApi
         public ViewStructureImpl(@NonNull AutofillId parentId, long virtualId, int sessionId) {
-            mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+            mNode.mParentAutofillId = Objects.requireNonNull(parentId);
             mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
         }
 
@@ -830,7 +830,7 @@
 
         @Override
         public void setTextIdEntry(@NonNull String entryName) {
-            mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
+            mNode.mTextIdEntry = Objects.requireNonNull(entryName);
         }
 
         @Override
@@ -840,7 +840,7 @@
 
         @Override
         public void setHintIdEntry(String entryName) {
-            mNode.mHintIdEntry = Preconditions.checkNotNull(entryName);
+            mNode.mHintIdEntry = Objects.requireNonNull(entryName);
         }
 
         @Override
@@ -913,13 +913,13 @@
 
         @Override
         public void setAutofillId(AutofillId id) {
-            mNode.mAutofillId = Preconditions.checkNotNull(id);
+            mNode.mAutofillId = Objects.requireNonNull(id);
         }
 
 
         @Override
         public void setAutofillId(AutofillId parentId, int virtualId) {
-            mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+            mNode.mParentAutofillId = Objects.requireNonNull(parentId);
             mNode.mAutofillId = new AutofillId(parentId, virtualId);
         }
 
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 1eb1a93..e1e1755 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,6 +111,22 @@
     private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
 
     /**
+     * Whether the IME supports inline suggestions from the default Autofill service that
+     * provides the input view.
+     *
+     * Note: The default value is {@code true}.
+     */
+    private boolean mServiceSupported;
+
+    /**
+     * Whether the IME supports inline suggestions from the application that provides the
+     * input view.
+     *
+     * Note: The default value is {@code true}.
+     */
+    private boolean mClientSupported;
+
+    /**
      * @hide
      * @see {@link #mHostInputToken}.
      */
@@ -204,6 +220,14 @@
         return Bundle.EMPTY;
     }
 
+    private static boolean defaultServiceSupported() {
+        return true;
+    }
+
+    private static boolean defaultClientSupported() {
+        return true;
+    }
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setInlinePresentationSpecs(
@@ -216,15 +240,25 @@
         abstract Builder setHostDisplayId(int value);
     }
 
+    /** @hide */
+    public boolean isServiceSupported() {
+        return mServiceSupported;
+    }
+
+    /** @hide */
+    public boolean isClientSupported() {
+        return mClientSupported;
+    }
 
 
-    // Code below generated by codegen v1.0.23.
+
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -240,7 +274,9 @@
             @NonNull Bundle extras,
             @Nullable IBinder hostInputToken,
             int hostDisplayId,
-            @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
+            @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+            boolean serviceSupported,
+            boolean clientSupported) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mInlinePresentationSpecs = inlinePresentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
@@ -257,6 +293,8 @@
         this.mHostInputToken = hostInputToken;
         this.mHostDisplayId = hostDisplayId;
         this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+        this.mServiceSupported = serviceSupported;
+        this.mClientSupported = clientSupported;
 
         onConstructed();
     }
@@ -340,7 +378,9 @@
     }
 
     /**
-     * Specifies the UI specification for the inline suggestion tooltip in the response.
+     * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+     *
+     * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
      */
     @DataClass.Generated.Member
     public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -361,7 +401,9 @@
                 "extras = " + mExtras + ", " +
                 "hostInputToken = " + mHostInputToken + ", " +
                 "hostDisplayId = " + mHostDisplayId + ", " +
-                "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
+                "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+                "serviceSupported = " + mServiceSupported + ", " +
+                "clientSupported = " + mClientSupported +
         " }";
     }
 
@@ -385,7 +427,9 @@
                 && extrasEquals(that.mExtras)
                 && java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
                 && mHostDisplayId == that.mHostDisplayId
-                && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
+                && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+                && mServiceSupported == that.mServiceSupported
+                && mClientSupported == that.mClientSupported;
     }
 
     @Override
@@ -403,6 +447,8 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         _hash = 31 * _hash + mHostDisplayId;
         _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+        _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+        _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
         return _hash;
     }
 
@@ -413,6 +459,8 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         int flg = 0;
+        if (mServiceSupported) flg |= 0x100;
+        if (mClientSupported) flg |= 0x200;
         if (mHostInputToken != null) flg |= 0x20;
         if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
         dest.writeInt(flg);
@@ -438,6 +486,8 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         int flg = in.readInt();
+        boolean serviceSupported = (flg & 0x100) != 0;
+        boolean clientSupported = (flg & 0x200) != 0;
         int maxSuggestionCount = in.readInt();
         List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
         in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
@@ -464,6 +514,8 @@
         this.mHostInputToken = hostInputToken;
         this.mHostDisplayId = hostDisplayId;
         this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+        this.mServiceSupported = serviceSupported;
+        this.mClientSupported = clientSupported;
 
         onConstructed();
     }
@@ -497,6 +549,8 @@
         private @Nullable IBinder mHostInputToken;
         private int mHostDisplayId;
         private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+        private boolean mServiceSupported;
+        private boolean mClientSupported;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -629,7 +683,9 @@
         }
 
         /**
-         * Specifies the UI specification for the inline suggestion tooltip in the response.
+         * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+         *
+         * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
          */
         @DataClass.Generated.Member
         public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -639,10 +695,38 @@
             return this;
         }
 
+        /**
+         * Whether the IME supports inline suggestions from the default Autofill service that
+         * provides the input view.
+         *
+         * Note: The default value is {@code true}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setServiceSupported(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100;
+            mServiceSupported = value;
+            return this;
+        }
+
+        /**
+         * Whether the IME supports inline suggestions from the application that provides the
+         * input view.
+         *
+         * Note: The default value is {@code true}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setClientSupported(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x200;
+            mClientSupported = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x100; // Mark builder used
+            mBuilderFieldsSet |= 0x400; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -665,6 +749,12 @@
             if ((mBuilderFieldsSet & 0x80) == 0) {
                 mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
             }
+            if ((mBuilderFieldsSet & 0x100) == 0) {
+                mServiceSupported = defaultServiceSupported();
+            }
+            if ((mBuilderFieldsSet & 0x200) == 0) {
+                mClientSupported = defaultClientSupported();
+            }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
                     mInlinePresentationSpecs,
@@ -673,12 +763,14 @@
                     mExtras,
                     mHostInputToken,
                     mHostDisplayId,
-                    mInlineTooltipPresentationSpec);
+                    mInlineTooltipPresentationSpec,
+                    mServiceSupported,
+                    mClientSupported);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x100) != 0) {
+            if ((mBuilderFieldsSet & 0x400) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -686,10 +778,10 @@
     }
 
     @DataClass.Generated(
-            time = 1621415989607L,
-            codegenVersion = "1.0.23",
+            time = 1615798784918L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate  boolean mServiceSupported\nprivate  boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static  boolean defaultServiceSupported()\nprivate static  boolean defaultClientSupported()\npublic  boolean isServiceSupported()\npublic  boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index d2db0df..5b2068f 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -96,8 +96,6 @@
      *
      * @param token special token for the system to identify
      *              {@link InputMethodService}
-     * @param displayId The id of the display that current IME shown.
-     *                  Used for {{@link #updateInputMethodDisplay(int)}}
      * @param privilegedOperations IPC endpoint to do some privileged
      *                             operations that are allowed only to the
      *                             current IME.
@@ -105,9 +103,8 @@
      * @hide
      */
     @MainThread
-    default void initializeInternal(IBinder token, int displayId,
+    default void initializeInternal(IBinder token,
             IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
-        updateInputMethodDisplay(displayId);
         attachToken(token);
     }
 
@@ -143,16 +140,6 @@
     public void attachToken(IBinder token);
 
     /**
-     * Update context display according to given displayId.
-     *
-     * @param displayId The id of the display that need to update for context.
-     * @hide
-     */
-    @MainThread
-    default void updateInputMethodDisplay(int displayId) {
-    }
-
-    /**
      * Bind a new application environment in to the input method, so that it
      * can later start and stop input processing.
      * Typically this method is called when this input method is enabled in an
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 42d77cd..ce92309 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.util.imetracing.ImeTracing.PROTO_ARG;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
@@ -73,7 +72,6 @@
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.ImeFocusController;
@@ -88,6 +86,7 @@
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -367,7 +366,7 @@
     final H mH;
 
     // Our generic input connection if the current target does not have its own.
-    final IInputContext mIInputContext;
+    private final IInputConnectionWrapper mIInputContext;
 
     private final int mDisplayId;
 
@@ -409,7 +408,6 @@
     /**
      * The InputConnection that was last retrieved from the served view.
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     IInputConnectionWrapper mServedInputConnectionWrapper;
     /**
      * The completions that were last provided by the served view.
@@ -921,12 +919,9 @@
                             // that this happened and make sure our own editor's
                             // state is reset.
                             mRestartOnNextWindowFocus = true;
-                            try {
-                                // Note that finishComposingText() is allowed to run
-                                // even when we are not active.
-                                mIInputContext.finishComposingText();
-                            } catch (RemoteException e) {
-                            }
+                            // Note that finishComposingText() is allowed to run
+                            // even when we are not active.
+                            mIInputContext.finishComposingText();
                         }
                         // Check focus again in case that "onWindowFocus" is called before
                         // handling this message.
@@ -958,9 +953,10 @@
                     final boolean fullscreen = msg.arg1 != 0;
                     InputConnection ic = null;
                     synchronized (mH) {
-                        mFullscreenMode = fullscreen;
-                        if (mServedInputConnectionWrapper != null) {
+                        if (mFullscreenMode != fullscreen
+                                && mServedInputConnectionWrapper != null) {
                             ic = mServedInputConnectionWrapper.getInputConnection();
+                            mFullscreenMode = fullscreen;
                         }
                     }
                     if (ic != null) {
@@ -1205,14 +1201,27 @@
         }
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * No longer used.  Do not use.
+     *
+     * TODO(b/192412909) Remove this method.
+     *
+     * @deprecated Was kept due to {@link UnsupportedAppUsage}.  Will be removed soon.
+     * @hide
+     */
     public IInputMethodClient getClient() {
         return mClient;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * No longer used.  Do not use.
+     *
+     * TODO(b/192412909) Remove this method.
+     *
+     * @deprecated Was kept due to {@link UnsupportedAppUsage}.  Will be removed soon.
+     * @hide
+     */
+    @Deprecated
     public IInputContext getInputContext() {
         return mIInputContext;
     }
@@ -3192,7 +3201,7 @@
         }
 
         for (String arg : args) {
-            if (arg.equals(PROTO_ARG)) {
+            if (arg.equals(ImeTracing.PROTO_ARG)) {
                 final ProtoOutputStream proto = new ProtoOutputStream(fd);
                 dumpDebug(proto, null /* icProto */);
                 proto.flush();
@@ -3211,7 +3220,7 @@
      * @hide
      */
     @GuardedBy("mH")
-    public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) {
+    public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) {
         if (mCurrentInputMethodSession == null) {
             return;
         }
@@ -3237,7 +3246,7 @@
                 mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION);
             }
             if (icProto != null) {
-                proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
+                proto.write(INPUT_CONNECTION_CALL, icProto);
             }
         }
     }
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 1e8253d..604979b 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -27,10 +27,10 @@
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Represents the result of language detection of a piece of text.
@@ -168,7 +168,7 @@
         public Builder putLocale(
                 @NonNull ULocale locale,
                 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
-            Preconditions.checkNotNull(locale);
+            Objects.requireNonNull(locale);
             mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
             return this;
         }
@@ -187,7 +187,7 @@
          */
         @NonNull
         public Builder setExtras(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
+            mBundle = Objects.requireNonNull(bundle);
             return this;
         }
 
@@ -316,7 +316,7 @@
              * @param text the text to process.
              */
             public Builder(@NonNull CharSequence text) {
-                mText = Preconditions.checkNotNull(text);
+                mText = Objects.requireNonNull(text);
             }
 
             /**
@@ -324,7 +324,7 @@
              */
             @NonNull
             public Builder setExtras(@NonNull Bundle bundle) {
-                mBundle = Preconditions.checkNotNull(bundle);
+                mBundle = Objects.requireNonNull(bundle);
                 return this;
             }
 
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index be49fc4..91412d7 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -178,12 +178,23 @@
      */
     static void reserveAddressSpaceInZygote() {
         System.loadLibrary("webviewchromium_loader");
-        boolean is64Bit = VMRuntime.getRuntime().is64Bit();
-        // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
-        // On 32-bit it's fairly scarce and we should keep it to a realistic number that
-        // permits some future growth but doesn't hog space: we use 130MB which is roughly
-        // what was calculated on older OS versions in practice.
-        long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024;
+        long addressSpaceToReserve;
+        if (VMRuntime.getRuntime().is64Bit()) {
+            // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
+            addressSpaceToReserve = 1 * 1024 * 1024 * 1024;
+        } else if (VMRuntime.getRuntime().vmInstructionSet().equals("arm")) {
+            // On 32-bit the address space is fairly scarce, hence we should keep it to a realistic
+            // number that permits some future growth but doesn't hog space. For ARM we use 130MB
+            // which is roughly what was calculated on older OS versions. The size has been
+            // growing gradually, but a few efforts have offset it back to the size close to the
+            // original.
+            addressSpaceToReserve = 130 * 1024 * 1024;
+        } else {
+            // The number below was obtained for a binary used for x86 emulators, allowing some
+            // natural growth.
+            addressSpaceToReserve = 190 * 1024 * 1024;
+        }
+
         sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
 
         if (sAddressSpaceReserved) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index f9f823b..26579c5d 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -480,6 +480,17 @@
             return false;
         }
 
+        /**
+         * @see View#onApplyWindowInsets(WindowInsets).
+         *
+         * <p>This is the entry point for the WebView implementation to override. It returns
+         * {@code null} when the WebView implementation hasn't implemented the WindowInsets support
+         * on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be
+         * called instead.
+         *
+         * @param insets Insets to apply
+         * @return The supplied insets with any applied insets consumed.
+         */
         @SuppressWarnings("unused")
         @Nullable
         default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index d8f7f4c..f3dfda5 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -412,24 +412,29 @@
     }
 
     void refreshChildren() {
-        if (mAdapter == null) return;
+        final int adapterCount = mAdapter == null ? 0 : getCount();
         for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
             int index = modulo(i, getWindowSize());
 
-            int adapterCount = getCount();
-            // get the fresh child from the adapter
-            final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+            final View updatedChild;
+            if (i < adapterCount) {
+                // get the fresh child from the adapter
+                updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
 
-            if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-                updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+                if (updatedChild.getImportantForAccessibility()
+                        == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                    updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+                }
+            } else {
+                updatedChild = null;
             }
 
             if (mViewsMap.containsKey(index)) {
                 final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
-                // add the new child to the frame, if it exists
+                // flush out the old child
+                fl.removeAllViewsInLayout();
                 if (updatedChild != null) {
-                    // flush out the old child
-                    fl.removeAllViewsInLayout();
+                    // add the new child to the frame, if it exists
                     fl.addView(updatedChild);
                 }
             }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0c4da88..62585c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -126,13 +126,13 @@
 import android.widget.TextView.OnEditorActionListener;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.view.FloatingActionMode;
-import com.android.internal.widget.EditableInputConnection;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 8aa557b..f292c61 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -136,6 +136,7 @@
 
     private int[] mState = null;
     private boolean mMergeState = false;
+    private boolean mHasLevelSet = false;
     private int mLevel = 0;
     @UnsupportedAppUsage
     private int mDrawableWidth;
@@ -798,6 +799,7 @@
     @android.view.RemotableViewMethod
     public void setImageLevel(int level) {
         mLevel = level;
+        mHasLevelSet = true;
         if (mDrawable != null) {
             mDrawable.setLevel(level);
             resizeFromDrawable();
@@ -1069,7 +1071,9 @@
                         : isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
                 d.setVisible(visible, true);
             }
-            d.setLevel(mLevel);
+            if (mHasLevelSet) {
+                d.setLevel(mLevel);
+            }
             mDrawableWidth = d.getIntrinsicWidth();
             mDrawableHeight = d.getIntrinsicHeight();
             applyImageTint();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1b76ebf..356d059 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -245,8 +245,6 @@
 
     private boolean mAggregatedIsVisible;
 
-    private CharSequence mCustomStateDescription = null;
-
     private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
 
     private ObjectAnimator mLastProgressAnimator;
@@ -685,6 +683,9 @@
                 swapCurrentDrawable(mProgressDrawable);
                 stopAnimation();
             }
+
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -1622,18 +1623,21 @@
     @Override
     @RemotableViewMethod
     public void setStateDescription(@Nullable CharSequence stateDescription) {
-        mCustomStateDescription = stateDescription;
-        if (stateDescription == null) {
-            super.setStateDescription(formatStateDescription(mProgress));
-        } else {
-            super.setStateDescription(stateDescription);
-        }
+        // Assume the previous custom state description is different from default state description.
+        // Otherwise when the argument is null to restore the default state description, we will
+        // send out a state description changed event even though the state description presented to
+        // the user doesn't change. Since mStateDescription in View is private, we can't prevent
+        // this event from sending out.
+        super.setStateDescription(stateDescription);
     }
 
     void onProgressRefresh(float scale, boolean fromUser, int progress) {
         if (AccessibilityManager.getInstance(mContext).isEnabled()
-                && mCustomStateDescription == null) {
-            super.setStateDescription(formatStateDescription(mProgress));
+                && getStateDescription() == null && !isIndeterminate()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+            event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
+            sendAccessibilityEventUnchecked(event);
         }
     }
 
@@ -2356,7 +2360,15 @@
                     AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
                     getProgress());
             info.setRangeInfo(rangeInfo);
-            info.setStateDescription(formatStateDescription(mProgress));
+        }
+
+        // Only set the default state description when custom state descripton is null.
+        if (getStateDescription() == null) {
+            if (isIndeterminate()) {
+                info.setStateDescription(getResources().getString(R.string.in_progress));
+            } else {
+                info.setStateDescription(formatStateDescription(mProgress));
+            }
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ca6e735..5500ff0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -200,13 +200,14 @@
 import android.view.translation.ViewTranslationRequest;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastMath;
 import com.android.internal.util.Preconditions;
-import com.android.internal.widget.EditableInputConnection;
 
 import libcore.util.EmptyArray;
 
@@ -6318,6 +6319,11 @@
             text = TextUtils.stringOrSpannedString(text);
         }
 
+        @AccessibilityUtils.A11yTextChangeType int a11yTextChangeType = AccessibilityUtils.NONE;
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            a11yTextChangeType = AccessibilityUtils.textOrSpanChanged(text, mText);
+        }
+
         if (mAutoLinkMask != 0) {
             Spannable s2;
 
@@ -6337,6 +6343,9 @@
                  * setText() again to try to upgrade the buffer type.
                  */
                 setTextInternal(text);
+                if (a11yTextChangeType == AccessibilityUtils.NONE) {
+                    a11yTextChangeType = AccessibilityUtils.PARCELABLE_SPAN;
+                }
 
                 // Do not change the movement method for text that support text selection as it
                 // would prevent an arbitrary cursor displacement.
@@ -6401,7 +6410,13 @@
         sendOnTextChanged(text, 0, oldlen, textLength);
         onTextChanged(text, 0, oldlen, textLength);
 
-        notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+        if (a11yTextChangeType == AccessibilityUtils.TEXT) {
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+        } else if (a11yTextChangeType == AccessibilityUtils.PARCELABLE_SPAN) {
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+        }
 
         if (needEditableForNotification) {
             sendAfterTextChanged((Editable) text);
diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java
new file mode 100644
index 0000000..9a07975
--- /dev/null
+++ b/core/java/android/window/ConfigurationHelper.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.view.Display.INVALID_DISPLAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * A helper class to maintain {@link android.content.res.Configuration} related methods used both
+ * in {@link android.app.Activity} and {@link WindowContext}.
+ *
+ * @hide
+ */
+public class ConfigurationHelper {
+    private ConfigurationHelper() {}
+
+    /** Ask text layout engine to free its caches if there is a locale change. */
+    public static void freeTextLayoutCachesIfNeeded(int configDiff) {
+        if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            Canvas.freeTextLayoutCaches();
+        }
+    }
+
+    /**
+     * A helper method to filter out {@link ActivityInfo#CONFIG_SCREEN_SIZE} if the
+     * {@link Configuration#diffPublicOnly(Configuration) diff} of two {@link Configuration}
+     * doesn't cross the boundary.
+     *
+     * @see SizeConfigurationBuckets#filterDiff(int, Configuration, Configuration,
+     * SizeConfigurationBuckets)
+     */
+    public static int diffPublicWithSizeBuckets(@Nullable Configuration currentConfig,
+            @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
+        // If current configuration is null, it is definitely different from updated Configuration.
+        if (currentConfig == null) {
+            return 0xffffffff;
+        }
+        int publicDiff = currentConfig.diffPublicOnly(newConfig);
+        return SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, newConfig, buckets);
+    }
+
+    /**
+     * Returns {@code true} if the {@link android.content.res.Resources} associated with
+     * a {@code token} needs to be updated.
+     *
+     * @param token A {@link Context#getActivityToken() activity token} or
+     * {@link Context#getWindowContextToken() window context token}
+     * @param config The original {@link Configuration}
+     * @param newConfig The updated Configuration
+     * @param displayChanged a flag to indicate there's a display change
+     * @param configChanged a flag to indicate there's a Configuration change.
+     *
+     * @see ResourcesManager#updateResourcesForActivity(IBinder, Configuration, int)
+     */
+    public static boolean shouldUpdateResources(IBinder token, @Nullable Configuration config,
+            @NonNull Configuration newConfig, @NonNull Configuration overrideConfig,
+            boolean displayChanged, @Nullable Boolean configChanged) {
+        // The configuration has not yet been initialized. We should update it.
+        if (config == null) {
+            return true;
+        }
+        // If the token associated context is moved to another display, we should update the
+        // ResourcesKey.
+        if (displayChanged) {
+            return true;
+        }
+        // If the new config is the same as the config this Activity is already running with and
+        // the override config also didn't change, then don't update the Resources
+        if (!ResourcesManager.getInstance().isSameResourcesOverrideConfig(token, overrideConfig)) {
+            return true;
+        }
+        // If there's a update on WindowConfiguration#mBounds or maxBounds, we should update the
+        // Resources to make WindowMetrics API report the updated result.
+        if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
+            return true;
+        }
+        return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
+    }
+
+    /**
+     * Returns {@code true} if {@code displayId} is different from {@code newDisplayId}.
+     * Note that {@link Display#INVALID_DISPLAY} means no difference.
+     */
+    public static boolean isDifferentDisplay(int displayId, int newDisplayId) {
+        return newDisplayId != INVALID_DISPLAY && displayId != newDisplayId;
+    }
+
+    // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
+    // constructions.
+    /**
+     * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
+     * should be updated.
+     *
+     * @see WindowManager#getCurrentWindowMetrics()
+     * @see WindowManager#getMaximumWindowMetrics()
+     */
+    private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
+            @NonNull Configuration newConfig) {
+        final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
+        final Rect newBounds = newConfig.windowConfiguration.getBounds();
+
+        final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
+        final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
+
+        return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
+    }
+}
diff --git a/core/java/android/window/DisplayAreaInfo.java b/core/java/android/window/DisplayAreaInfo.java
index 358467f..1a7aab6 100644
--- a/core/java/android/window/DisplayAreaInfo.java
+++ b/core/java/android/window/DisplayAreaInfo.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.content.res.Configuration;
@@ -43,8 +45,17 @@
      */
     public final int displayId;
 
+    /**
+     * The feature id of this display area.
+     */
     public final int featureId;
 
+    /**
+     * The feature id of the root display area this display area is associated with.
+     * @hide
+     */
+    public int rootDisplayAreaId = FEATURE_UNDEFINED;
+
     public DisplayAreaInfo(@NonNull WindowContainerToken token, int displayId, int featureId) {
         this.token = token;
         this.displayId = displayId;
@@ -56,6 +67,7 @@
         configuration.readFromParcel(in);
         displayId = in.readInt();
         featureId = in.readInt();
+        rootDisplayAreaId = in.readInt();
     }
 
     @Override
@@ -64,6 +76,7 @@
         configuration.writeToParcel(dest, flags);
         dest.writeInt(displayId);
         dest.writeInt(featureId);
+        dest.writeInt(rootDisplayAreaId);
     }
 
     @NonNull
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 8784399..6758a3b 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -34,6 +34,15 @@
 public class DisplayAreaOrganizer extends WindowOrganizer {
 
     /**
+     * Key to specify the {@link com.android.server.wm.RootDisplayArea} to attach a window to.
+     * It will be used by the function passed in from
+     * {@link com.android.server.wm.DisplayAreaPolicyBuilder#setSelectRootForWindowFunc(BiFunction)}
+     * to find the Root DA to attach the window.
+     * @hide
+     */
+    public static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
+
+    /**
      * The value in display area indicating that no value has been set.
      */
     public static final int FEATURE_UNDEFINED = -1;
@@ -256,6 +265,7 @@
         }
     };
 
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
     private IDisplayAreaOrganizerController getController() {
         try {
             return getWindowOrganizerController().getDisplayAreaOrganizerController();
@@ -263,5 +273,4 @@
             return null;
         }
     }
-
 }
diff --git a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
index 02aa1a9..7864c24 100644
--- a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
+++ b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
@@ -16,14 +16,18 @@
 
 package android.window;
 
+import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
 /**
  * Interface to be invoked by the controlling process when a remote transition has finished.
  *
  * @see IRemoteTransition
+ * @param wct An optional WindowContainerTransaction to apply before the transition finished.
+ * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup
+ *            transaction. This is applied by shell.Transitions (before submitting the wct).
  * {@hide}
  */
 interface IRemoteTransitionFinishedCallback {
-    void onTransitionFinished(in WindowContainerTransaction wct);
+    void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct);
 }
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
new file mode 100644
index 0000000..5eb432e
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+/** @hide */
+oneway interface ITaskFragmentOrganizer {
+    void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+    void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
+    void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
+
+    /**
+     * Called when the parent leaf Task of organized TaskFragments is changed.
+     * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+     * transaction.
+     *
+     * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+     * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+     * bounds.
+     */
+    void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
+
+    /**
+     * Called when the {@link WindowContainerTransaction} created with
+     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+     *
+     * @param errorCallbackToken    Token set through {@link
+     *                              WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+     * @param exceptionBundle   Bundle containing the exception. Should be created with
+     *                          {@link TaskFragmentOrganizer#putExceptionInBundle}.
+     */
+    void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
new file mode 100644
index 0000000..0ca8a86
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.window.ITaskFragmentOrganizer;
+
+/** @hide */
+interface ITaskFragmentOrganizerController {
+
+    /**
+     * Registers a TaskFragmentOrganizer to manage TaskFragments.
+     */
+    void registerOrganizer(in ITaskFragmentOrganizer organizer);
+
+    /**
+     * Unregisters a previously registered TaskFragmentOrganizer.
+     */
+    void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+}
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 1223d72..e65fcdd 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -19,7 +19,9 @@
 import android.view.SurfaceControl;
 
 import android.os.IBinder;
+import android.view.RemoteAnimationAdapter;
 import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizerController;
 import android.window.ITaskOrganizerController;
 import android.window.ITransitionPlayer;
 import android.window.IWindowContainerTransactionCallback;
@@ -60,6 +62,17 @@
             in @nullable WindowContainerTransaction t);
 
     /**
+     * Starts a legacy transition.
+     * @param type The transition type.
+     * @param adapter The animation to use.
+     * @param syncCallback A sync callback for the contents of `t`
+     * @param t Operations that are part of the transition.
+     * @return sync-id or -1 if this no-op'd because a transition is already running.
+     */
+    int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
+            in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
+
+    /**
      * Finishes a transition. This must be called for all created transitions.
      * @param transitionToken Which transition to finish
      * @param t Changes to make before finishing but in the same SF Transaction. Can be null.
@@ -77,6 +90,9 @@
     /** @return An interface enabling the management of display area organizers. */
     IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
 
+    /** @return An interface enabling the management of task fragment organizers. */
+    ITaskFragmentOrganizerController getTaskFragmentOrganizerController();
+
     /**
      * Registers a transition player with Core. There is only one of these at a time and calling
      * this will replace the existing one if set.
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/window/TaskFragmentAppearedInfo.aidl
new file mode 100644
index 0000000..3729c09
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAppearedInfo.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
+ */
+parcelable TaskFragmentAppearedInfo;
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
new file mode 100644
index 0000000..234b30c
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAppearedInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
+ */
+public final class TaskFragmentAppearedInfo implements Parcelable {
+
+    @NonNull
+    private final TaskFragmentInfo mTaskFragmentInfo;
+
+    @NonNull
+    private final SurfaceControl mLeash;
+
+    public TaskFragmentAppearedInfo(
+            @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
+        mTaskFragmentInfo = taskFragmentInfo;
+        mLeash = leash;
+    }
+
+    public TaskFragmentInfo getTaskFragmentInfo() {
+        return mTaskFragmentInfo;
+    }
+
+    public SurfaceControl getLeash() {
+        return mLeash;
+    }
+
+    private TaskFragmentAppearedInfo(Parcel in) {
+        mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+        mLeash = in.readTypedObject(SurfaceControl.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mTaskFragmentInfo, flags);
+        dest.writeTypedObject(mLeash, flags);
+    }
+
+    @NonNull
+    public static final Creator<TaskFragmentAppearedInfo> CREATOR =
+            new Creator<TaskFragmentAppearedInfo>() {
+                @Override
+                public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
+                    return new TaskFragmentAppearedInfo(in);
+                }
+
+                @Override
+                public TaskFragmentAppearedInfo[] newArray(int size) {
+                    return new TaskFragmentAppearedInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "TaskFragmentAppearedInfo{"
+                + " taskFragmentInfo=" + mTaskFragmentInfo
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/window/TaskFragmentCreationParams.aidl b/core/java/android/window/TaskFragmentCreationParams.aidl
new file mode 100644
index 0000000..fde5089
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+parcelable TaskFragmentCreationParams;
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java
new file mode 100644
index 0000000..e4d6a6c
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WindowingMode;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+public final class TaskFragmentCreationParams implements Parcelable {
+
+    /** The organizer that will organize this TaskFragment. */
+    @NonNull
+    private final ITaskFragmentOrganizer mOrganizer;
+
+    /**
+     * Unique token assigned from the client organizer to identify the {@link TaskFragmentInfo} when
+     * a new TaskFragment is created with this option.
+     */
+    @NonNull
+    private final IBinder mFragmentToken;
+
+    /**
+     * Activity token used to identify the leaf Task to create the TaskFragment in. It has to belong
+     * to the same app as the root Activity of the target Task.
+     */
+    @NonNull
+    private final IBinder mOwnerToken;
+
+    /** The initial bounds of the TaskFragment. Fills parent if empty. */
+    @NonNull
+    private final Rect mInitialBounds = new Rect();
+
+    /** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */
+    @WindowingMode
+    private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+    private TaskFragmentCreationParams(
+            @NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken,
+            @NonNull IBinder ownerToken) {
+        mOrganizer = organizer;
+        mFragmentToken = fragmentToken;
+        mOwnerToken = ownerToken;
+    }
+
+    public ITaskFragmentOrganizer getOrganizer() {
+        return mOrganizer;
+    }
+
+    public IBinder getFragmentToken() {
+        return mFragmentToken;
+    }
+
+    public IBinder getOwnerToken() {
+        return mOwnerToken;
+    }
+
+    public Rect getInitialBounds() {
+        return mInitialBounds;
+    }
+
+    @WindowingMode
+    public int getWindowingMode() {
+        return mWindowingMode;
+    }
+
+    private TaskFragmentCreationParams(Parcel in) {
+        mOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
+        mFragmentToken = in.readStrongBinder();
+        mOwnerToken = in.readStrongBinder();
+        mInitialBounds.readFromParcel(in);
+        mWindowingMode = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongInterface(mOrganizer);
+        dest.writeStrongBinder(mFragmentToken);
+        dest.writeStrongBinder(mOwnerToken);
+        mInitialBounds.writeToParcel(dest, flags);
+        dest.writeInt(mWindowingMode);
+    }
+
+    @NonNull
+    public static final Creator<TaskFragmentCreationParams> CREATOR =
+            new Creator<TaskFragmentCreationParams>() {
+                @Override
+                public TaskFragmentCreationParams createFromParcel(Parcel in) {
+                    return new TaskFragmentCreationParams(in);
+                }
+
+                @Override
+                public TaskFragmentCreationParams[] newArray(int size) {
+                    return new TaskFragmentCreationParams[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "TaskFragmentCreationParams{"
+                + " organizer=" + mOrganizer
+                + " fragmentToken=" + mFragmentToken
+                + " ownerToken=" + mOwnerToken
+                + " initialBounds=" + mInitialBounds
+                + " windowingMode=" + mWindowingMode
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Builder to construct the options to create TaskFragment with. */
+    public static class Builder {
+
+        @NonNull
+        private final ITaskFragmentOrganizer mOrganizer;
+
+        @NonNull
+        private final IBinder mFragmentToken;
+
+        @NonNull
+        private final IBinder mOwnerToken;
+
+        @NonNull
+        private final Rect mInitialBounds = new Rect();
+
+        @WindowingMode
+        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+        public Builder(@NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken,
+                @NonNull IBinder ownerToken) {
+            mOrganizer = organizer;
+            mFragmentToken = fragmentToken;
+            mOwnerToken = ownerToken;
+        }
+
+        /** Sets the initial bounds for the TaskFragment. */
+        public Builder setInitialBounds(@NonNull Rect bounds) {
+            mInitialBounds.set(bounds);
+            return this;
+        }
+
+        /** Sets the initial windowing mode for the TaskFragment. */
+        public Builder setWindowingMode(@WindowingMode int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        /** Constructs the options to create TaskFragment with. */
+        public TaskFragmentCreationParams build() {
+            final TaskFragmentCreationParams result = new TaskFragmentCreationParams(
+                    mOrganizer, mFragmentToken, mOwnerToken);
+            result.mInitialBounds.set(mInitialBounds);
+            result.mWindowingMode = mWindowingMode;
+            return result;
+        }
+    }
+}
diff --git a/core/java/android/window/TaskFragmentInfo.aidl b/core/java/android/window/TaskFragmentInfo.aidl
new file mode 100644
index 0000000..461a736
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+parcelable TaskFragmentInfo;
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
new file mode 100644
index 0000000..45975db
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.app.WindowConfiguration.WindowingMode;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+public final class TaskFragmentInfo implements Parcelable {
+
+    /**
+     * Client assigned unique token in {@link TaskFragmentCreationParams#mFragmentToken} to create
+     * this TaskFragment with.
+     */
+    @NonNull
+    private final IBinder mFragmentToken;
+
+    @NonNull
+    private final WindowContainerToken mToken;
+
+    @NonNull
+    private final Configuration mConfiguration = new Configuration();
+
+    /** Whether the TaskFragment contains any child Activity. */
+    private final boolean mIsEmpty;
+
+    /** Whether this TaskFragment is visible on the window hierarchy. */
+    private final boolean mIsVisible;
+
+    /**
+     * List of Activity tokens that are children of this TaskFragment. It only contains Activities
+     * that belong to the organizer process for security.
+     */
+    private final List<IBinder> mActivities = new ArrayList<>();
+
+    /** Relative position of the fragment's top left corner in the parent container. */
+    private final Point mPositionInParent;
+
+    public TaskFragmentInfo(
+            @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
+            @NonNull Configuration configuration, boolean isEmpty, boolean isVisible,
+            List<IBinder> activities, @NonNull Point positionInParent) {
+        mFragmentToken = requireNonNull(fragmentToken);
+        mToken = requireNonNull(token);
+        mConfiguration.setTo(configuration);
+        mIsEmpty = isEmpty;
+        mIsVisible = isVisible;
+        mActivities.addAll(activities);
+        mPositionInParent = requireNonNull(positionInParent);
+    }
+
+    public IBinder getFragmentToken() {
+        return mFragmentToken;
+    }
+
+    public WindowContainerToken getToken() {
+        return mToken;
+    }
+
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    public boolean isEmpty() {
+        return mIsEmpty;
+    }
+
+    public boolean isVisible() {
+        return mIsVisible;
+    }
+
+    public List<IBinder> getActivities() {
+        return mActivities;
+    }
+
+    /** Returns the relative position of the fragment's top left corner in the parent container. */
+    @NonNull
+    public Point getPositionInParent() {
+        return mPositionInParent;
+    }
+
+    @WindowingMode
+    public int getWindowingMode() {
+        return mConfiguration.windowConfiguration.getWindowingMode();
+    }
+
+    /**
+     * Returns {@code true} if the parameters that are important for task fragment organizers are
+     * equal between this {@link TaskFragmentInfo} and {@param that}.
+     */
+    public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) {
+        if (that == null) {
+            return false;
+        }
+
+        return mFragmentToken.equals(that.mFragmentToken)
+                && mToken.equals(that.mToken)
+                && mIsEmpty == that.mIsEmpty
+                && mIsVisible == that.mIsVisible
+                && getWindowingMode() == that.getWindowingMode()
+                && mActivities.equals(that.mActivities)
+                && mPositionInParent.equals(that.mPositionInParent);
+    }
+
+    private TaskFragmentInfo(Parcel in) {
+        mFragmentToken = in.readStrongBinder();
+        mToken = in.readTypedObject(WindowContainerToken.CREATOR);
+        mConfiguration.readFromParcel(in);
+        mIsEmpty = in.readBoolean();
+        mIsVisible = in.readBoolean();
+        in.readBinderList(mActivities);
+        mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mFragmentToken);
+        dest.writeTypedObject(mToken, flags);
+        mConfiguration.writeToParcel(dest, flags);
+        dest.writeBoolean(mIsEmpty);
+        dest.writeBoolean(mIsVisible);
+        dest.writeBinderList(mActivities);
+        dest.writeTypedObject(mPositionInParent, flags);
+    }
+
+    @NonNull
+    public static final Creator<TaskFragmentInfo> CREATOR =
+            new Creator<TaskFragmentInfo>() {
+                @Override
+                public TaskFragmentInfo createFromParcel(Parcel in) {
+                    return new TaskFragmentInfo(in);
+                }
+
+                @Override
+                public TaskFragmentInfo[] newArray(int size) {
+                    return new TaskFragmentInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "TaskFragmentInfo{"
+                + " fragmentToken=" + mFragmentToken
+                + " token=" + mToken
+                + " isEmpty=" + mIsEmpty
+                + " isVisible=" + mIsVisible
+                + " positionInParent=" + mPositionInParent
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
new file mode 100644
index 0000000..b252df7
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.CallSuper;
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Interface for WindowManager to delegate control of {@link com.android.server.wm.TaskFragment}.
+ * @hide
+ */
+public class TaskFragmentOrganizer extends WindowOrganizer {
+
+    /**
+     * Key to the exception in {@link Bundle} in {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+     */
+    private static final String KEY_ERROR_CALLBACK_EXCEPTION = "fragment_exception";
+
+    /**
+     * Creates a {@link Bundle} with an exception that can be passed to
+     * {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+     */
+    public static Bundle putExceptionInBundle(@NonNull Throwable exception) {
+        final Bundle exceptionBundle = new Bundle();
+        exceptionBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
+        return exceptionBundle;
+    }
+
+    /**
+     * Callbacks from WM Core are posted on this executor.
+     */
+    private final Executor mExecutor;
+
+    public TaskFragmentOrganizer(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+
+    /**
+     * Gets the executor to run callbacks on.
+     */
+    @NonNull
+    public Executor getExecutor() {
+        return mExecutor;
+    }
+
+    /**
+     * Registers a TaskFragmentOrganizer to manage TaskFragments.
+     */
+    @CallSuper
+    public void registerOrganizer() {
+        try {
+            getController().registerOrganizer(mInterface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a previously registered TaskFragmentOrganizer.
+     */
+    @CallSuper
+    public void unregisterOrganizer() {
+        try {
+            getController().unregisterOrganizer(mInterface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Called when a TaskFragment is created and organized by this organizer. */
+    public void onTaskFragmentAppeared(
+            @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+
+    /** Called when the status of an organized TaskFragment is changed. */
+    public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+    /** Called when an organized TaskFragment is removed. */
+    public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+    /**
+     * Called when the parent leaf Task of organized TaskFragments is changed.
+     * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+     * transaction.
+     *
+     * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+     * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+     * bounds.
+     */
+    public void onTaskFragmentParentInfoChanged(
+            @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
+
+    /**
+     * Called when the {@link WindowContainerTransaction} created with
+     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+     *
+     * @param errorCallbackToken    token set in
+     *                             {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+     * @param exception             exception from the server side.
+     */
+    public void onTaskFragmentError(
+            @NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
+
+    @Override
+    public void applyTransaction(@NonNull WindowContainerTransaction t) {
+        t.setTaskFragmentOrganizer(mInterface);
+        super.applyTransaction(t);
+    }
+
+    @Override
+    public int applySyncTransaction(@NonNull WindowContainerTransaction t,
+            @NonNull WindowContainerTransactionCallback callback) {
+        t.setTaskFragmentOrganizer(mInterface);
+        return super.applySyncTransaction(t, callback);
+    }
+
+    private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
+        @Override
+        public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+            mExecutor.execute(
+                    () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
+        }
+
+        @Override
+        public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+            mExecutor.execute(
+                    () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
+        }
+
+        @Override
+        public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+            mExecutor.execute(
+                    () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
+        }
+
+        @Override
+        public void onTaskFragmentParentInfoChanged(
+                @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
+            mExecutor.execute(
+                    () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
+                            fragmentToken, parentConfig));
+        }
+
+        @Override
+        public void onTaskFragmentError(
+                @NonNull IBinder errorCallbackToken, @NonNull Bundle exceptionBundle) {
+            mExecutor.execute(() -> TaskFragmentOrganizer.this.onTaskFragmentError(
+                    errorCallbackToken,
+                    (Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
+        }
+    };
+
+    public ITaskFragmentOrganizer getIOrganizer() {
+        return mInterface;
+    }
+
+    private ITaskFragmentOrganizerController getController() {
+        try {
+            return getWindowOrganizerController().getTaskFragmentOrganizerController();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index c7c91cd..6f250fc 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -222,6 +222,7 @@
         }
     }
 
+
     /**
      * Restarts the top activity in the given task by killing its process if it is visible.
      * @hide
@@ -289,6 +290,7 @@
         }
     };
 
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
     private ITaskOrganizerController getController() {
         try {
             return getWindowOrganizerController().getTaskOrganizerController();
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 141f47b..607e316 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -18,9 +18,12 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.WindowConfiguration;
+import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,6 +36,18 @@
  */
 public final class TransitionFilter implements Parcelable {
 
+    /** The associated requirement doesn't care about the z-order. */
+    public static final int CONTAINER_ORDER_ANY = 0;
+    /** The associated requirement only matches the top-most (z-order) container. */
+    public static final int CONTAINER_ORDER_TOP = 1;
+
+    /** @hide */
+    @IntDef(prefix = { "CONTAINER_ORDER_" }, value = {
+            CONTAINER_ORDER_ANY,
+            CONTAINER_ORDER_TOP,
+    })
+    public @interface ContainerOrder {}
+
     /**
      * When non-null: this is a list of transition types that this filter applies to. This filter
      * will fail for transitions that aren't one of these types.
@@ -126,6 +141,8 @@
     public static final class Requirement implements Parcelable {
         public int mActivityType = ACTIVITY_TYPE_UNDEFINED;
         public int[] mModes = null;
+        public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
+        public ComponentName mTopActivity;
 
         public Requirement() {
         }
@@ -133,6 +150,8 @@
         private Requirement(Parcel in) {
             mActivityType = in.readInt();
             mModes = in.createIntArray();
+            mOrder = in.readInt();
+            mTopActivity = in.readTypedObject(ComponentName.CREATOR);
         }
 
         /** Go through changes and find if at-least one change matches this filter */
@@ -143,12 +162,16 @@
                     // Only look at independent animating windows.
                     continue;
                 }
+                if (mOrder == CONTAINER_ORDER_TOP && i > 0) {
+                    continue;
+                }
                 if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                     if (change.getTaskInfo() == null
                             || change.getTaskInfo().getActivityType() != mActivityType) {
                         continue;
                     }
                 }
+                if (!matchesTopActivity(change.getTaskInfo())) continue;
                 if (mModes != null) {
                     boolean pass = false;
                     for (int m = 0; m < mModes.length; ++m) {
@@ -164,12 +187,20 @@
             return false;
         }
 
+        private boolean matchesTopActivity(ActivityManager.RunningTaskInfo info) {
+            if (mTopActivity == null) return true;
+            if (info == null) return false;
+            final ComponentName component = info.topActivity;
+            return mTopActivity.equals(component);
+        }
+
         /** Check if the request matches this filter. It may generate false positives */
         boolean matches(@NonNull TransitionRequestInfo request) {
-            // Can't check modes since the transition hasn't been built at this point.
+            // Can't check modes/order since the transition hasn't been built at this point.
             if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
             return request.getTriggerTask() != null
-                    && request.getTriggerTask().getActivityType() == mActivityType;
+                    && request.getTriggerTask().getActivityType() == mActivityType
+                    && matchesTopActivity(request.getTriggerTask());
         }
 
         @Override
@@ -177,6 +208,8 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(mActivityType);
             dest.writeIntArray(mModes);
+            dest.writeInt(mOrder);
+            dest.writeTypedObject(mTopActivity, flags);
         }
 
         @NonNull
@@ -209,7 +242,18 @@
                     out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i]));
                 }
             }
-            return out.append("]}").toString();
+            out.append("]").toString();
+            out.append(" order=" + containerOrderToString(mOrder));
+            out.append(" topActivity=").append(mTopActivity);
+            return out.toString();
         }
     }
+
+    private static String containerOrderToString(int order) {
+        switch (order) {
+            case CONTAINER_ORDER_ANY: return "ANY";
+            case CONTAINER_ORDER_TOP: return "TOP";
+        }
+        return "UNKNOWN(" + order + ")";
+    }
 }
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 23b8ee4..ebc66ef 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -16,6 +16,12 @@
 
 package android.window;
 
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -31,6 +37,7 @@
 import android.app.ActivityManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Surface;
@@ -80,8 +87,11 @@
     /** The container has voice session. */
     public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4;
 
+    /** The container is the display. */
+    public static final int FLAG_IS_DISPLAY = 1 << 5;
+
     /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
-    public static final int FLAG_FIRST_CUSTOM = 1 << 5;
+    public static final int FLAG_FIRST_CUSTOM = 1 << 6;
 
     /** @hide */
     @IntDef(prefix = { "FLAG_" }, value = {
@@ -91,6 +101,7 @@
             FLAG_TRANSLUCENT,
             FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT,
             FLAG_IS_VOICE_INTERACTION,
+            FLAG_IS_DISPLAY,
             FLAG_FIRST_CUSTOM
     })
     public @interface ChangeFlags {}
@@ -102,6 +113,8 @@
     private SurfaceControl mRootLeash;
     private final Point mRootOffset = new Point();
 
+    private AnimationOptions mOptions;
+
     /** @hide */
     public TransitionInfo(@WindowManager.TransitionOldType int type,
             @WindowManager.TransitionFlags int flags) {
@@ -116,6 +129,7 @@
         mRootLeash = new SurfaceControl();
         mRootLeash.readFromParcel(in);
         mRootOffset.readFromParcel(in);
+        mOptions = in.readTypedObject(AnimationOptions.CREATOR);
     }
 
     @Override
@@ -126,6 +140,7 @@
         dest.writeList(mChanges);
         mRootLeash.writeToParcel(dest, flags);
         mRootOffset.writeToParcel(dest, flags);
+        dest.writeTypedObject(mOptions, flags);
     }
 
     @NonNull
@@ -154,6 +169,10 @@
         mRootOffset.set(offsetLeft, offsetTop);
     }
 
+    public void setAnimationOptions(AnimationOptions options) {
+        mOptions = options;
+    }
+
     public int getType() {
         return mType;
     }
@@ -182,6 +201,10 @@
         return mRootOffset;
     }
 
+    public AnimationOptions getAnimationOptions() {
+        return mOptions;
+    }
+
     @NonNull
     public List<Change> getChanges() {
         return mChanges;
@@ -257,6 +280,9 @@
         if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) {
             sb.append((sb.length() == 0 ? "" : "|") + "IS_VOICE_INTERACTION");
         }
+        if ((flags & FLAG_IS_DISPLAY) != 0) {
+            sb.append((sb.length() == 0 ? "" : "|") + "IS_DISPLAY");
+        }
         if ((flags & FLAG_FIRST_CUSTOM) != 0) {
             sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
         }
@@ -484,4 +510,146 @@
                     + mStartRotation + "->" + mEndRotation + "}";
         }
     }
+
+    /** Represents animation options during a transition */
+    public static final class AnimationOptions implements Parcelable {
+
+        private int mType;
+        private int mEnterResId;
+        private int mExitResId;
+        private boolean mOverrideTaskTransition;
+        private String mPackageName;
+        private final Rect mTransitionBounds = new Rect();
+        private HardwareBuffer mThumbnail;
+
+        private AnimationOptions(int type) {
+            mType = type;
+        }
+
+        public AnimationOptions(Parcel in) {
+            mType = in.readInt();
+            mEnterResId = in.readInt();
+            mExitResId = in.readInt();
+            mOverrideTaskTransition = in.readBoolean();
+            mPackageName = in.readString();
+            mTransitionBounds.readFromParcel(in);
+            mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
+        }
+
+        public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
+                int exitResId, boolean overrideTaskTransition) {
+            AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
+            options.mPackageName = packageName;
+            options.mEnterResId = enterResId;
+            options.mExitResId = exitResId;
+            options.mOverrideTaskTransition = overrideTaskTransition;
+            return options;
+        }
+
+        public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width,
+                int height) {
+            AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL);
+            options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+            return options;
+        }
+
+        public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width,
+                int height) {
+            AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP);
+            options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+            return options;
+        }
+
+        public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb,
+                int startX, int startY, boolean scaleUp) {
+            AnimationOptions options = new AnimationOptions(
+                    scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN);
+            options.mTransitionBounds.set(startX, startY, startX, startY);
+            options.mThumbnail = srcThumb;
+            return options;
+        }
+
+        public static AnimationOptions makeCrossProfileAnimOptions() {
+            AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS);
+            return options;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        public int getEnterResId() {
+            return mEnterResId;
+        }
+
+        public int getExitResId() {
+            return mExitResId;
+        }
+
+        public boolean getOverrideTaskTransition() {
+            return mOverrideTaskTransition;
+        }
+
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        public Rect getTransitionBounds() {
+            return mTransitionBounds;
+        }
+
+        public HardwareBuffer getThumbnail() {
+            return mThumbnail;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeInt(mEnterResId);
+            dest.writeInt(mExitResId);
+            dest.writeBoolean(mOverrideTaskTransition);
+            dest.writeString(mPackageName);
+            mTransitionBounds.writeToParcel(dest, flags);
+            dest.writeTypedObject(mThumbnail, flags);
+        }
+
+        @NonNull
+        public static final Creator<AnimationOptions> CREATOR =
+                new Creator<AnimationOptions>() {
+                    @Override
+                    public AnimationOptions createFromParcel(Parcel in) {
+                        return new AnimationOptions(in);
+                    }
+
+                    @Override
+                    public AnimationOptions[] newArray(int size) {
+                        return new AnimationOptions[size];
+                    }
+                };
+
+        /** @hide */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @NonNull
+        private static String typeToString(int mode) {
+            switch(mode) {
+                case ANIM_CUSTOM: return "ANIM_CUSTOM";
+                case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
+                case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
+                case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
+                case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
+                case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
+                default: return "<unknown:" + mode + ">";
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+                    + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
+        }
+    }
 }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index c0af572..3c360fb 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.PendingIntent;
 import android.app.WindowConfiguration;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -34,6 +36,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Represents a collection of operations on some WindowContainers that should be applied all at
@@ -48,11 +51,19 @@
     // Flat list because re-order operations are order-dependent
     private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
 
+    @Nullable
+    private IBinder mErrorCallbackToken;
+
+    @Nullable
+    private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
     public WindowContainerTransaction() {}
 
     private WindowContainerTransaction(Parcel in) {
         in.readMap(mChanges, null /* loader */);
         in.readList(mHierarchyOps, null /* loader */);
+        mErrorCallbackToken = in.readStrongBinder();
+        mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
     }
 
     private Change getOrCreateChange(IBinder token) {
@@ -325,7 +336,8 @@
 
     /**
      * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
-     * be made invisible. This currently only applies to Task containers created by organizer.
+     * be made invisible. This currently only applies to TaskFragment containers created by
+     * organizer.
      * @param root1 the first root.
      * @param root2 the second root.
      */
@@ -378,6 +390,154 @@
     }
 
     /**
+     * Sends a pending intent in sync.
+     * @param sender The PendingIntent sender.
+     * @param intent The fillIn intent to patch over the sender's base intent.
+     * @param options bundle containing ActivityOptions for the task's top activity.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent,
+            @Nullable Bundle options) {
+        mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
+                .setLaunchOptions(options)
+                .setPendingIntent(sender)
+                .setActivityIntent(intent)
+                .build());
+        return this;
+    }
+
+    /**
+     * Creates a new TaskFragment with the given options.
+     * @param taskFragmentOptions the options used to create the TaskFragment.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction createTaskFragment(
+            @NonNull TaskFragmentCreationParams taskFragmentOptions) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT)
+                        .setTaskFragmentCreationOptions(taskFragmentOptions)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed.
+     * @param taskFragment  the TaskFragment to be removed.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction deleteTaskFragment(
+            @NonNull WindowContainerToken taskFragment) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT)
+                        .setContainer(taskFragment.asBinder())
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Starts an activity in the TaskFragment.
+     * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+     *                      {@link TaskFragmentCreationParams#fragmentToken}.
+     * @param activityIntent    intent to start the activity.
+     * @param activityOptions    ActivityOptions to start the activity with.
+     * @see android.content.Context#startActivity(Intent, Bundle).
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction startActivityInTaskFragment(
+            @NonNull IBinder fragmentToken, @NonNull Intent activityIntent,
+            @Nullable Bundle activityOptions) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT)
+                        .setContainer(fragmentToken)
+                        .setActivityIntent(activityIntent)
+                        .setLaunchOptions(activityOptions)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Moves an activity into the TaskFragment.
+     * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+     *                      {@link TaskFragmentCreationParams#fragmentToken}.
+     * @param activityToken activity to be reparented.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction reparentActivityToTaskFragment(
+            @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(
+                        HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT)
+                        .setReparentContainer(fragmentToken)
+                        .setContainer(activityToken)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * Reparents all children of one TaskFragment to another.
+     * @param oldParent children of this TaskFragment will be reparented.
+     * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the
+     *                  children will be moved to the leaf Task.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction reparentChildren(
+            @NonNull WindowContainerToken oldParent,
+            @Nullable WindowContainerToken newParent) {
+        final HierarchyOp hierarchyOp =
+                new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
+                        .setContainer(oldParent.asBinder())
+                        .setReparentContainer(newParent != null ? newParent.asBinder() : null)
+                        .build();
+        mHierarchyOps.add(hierarchyOp);
+        return this;
+    }
+
+    /**
+     * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+     * trigger callback with this {@param errorCallbackToken}.
+     * @param errorCallbackToken    client provided token that will be passed back as parameter in
+     *                              the callback if there is an error on the server side.
+     * @see ITaskFragmentOrganizer#onTaskFragmentError
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+        if (mErrorCallbackToken != null) {
+            throw new IllegalStateException("Can't set multiple error token for one transaction.");
+        }
+        mErrorCallbackToken = errorCallbackToken;
+        return this;
+    }
+
+    /**
+     * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
+     * When this is set, the server side will not check for the permission of
+     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
+     * contains operations that are allowed for this organizer, such as modifying TaskFragments that
+     * are organized by this organizer.
+     * @hide
+     */
+    @NonNull
+    WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+        if (mTaskFragmentOrganizer != null) {
+            throw new IllegalStateException("Can't set multiple organizers for one transaction.");
+        }
+        mTaskFragmentOrganizer = organizer;
+        return this;
+    }
+
+    /**
      * Merges another WCT into this one.
      * @param transfer When true, this will transfer everything from other potentially leaving
      *                 other in an unusable state. When false, other is left alone, but
@@ -398,6 +558,23 @@
             mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
                     : new HierarchyOp(other.mHierarchyOps.get(i)));
         }
+        if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
+                != other.mErrorCallbackToken) {
+            throw new IllegalArgumentException("Can't merge two WCTs with different error token");
+        }
+        final IBinder taskFragmentOrganizerAsBinder = mTaskFragmentOrganizer != null
+                ? mTaskFragmentOrganizer.asBinder()
+                : null;
+        final IBinder otherTaskFragmentOrganizerAsBinder = other.mTaskFragmentOrganizer != null
+                ? other.mTaskFragmentOrganizer.asBinder()
+                : null;
+        if (!Objects.equals(taskFragmentOrganizerAsBinder, otherTaskFragmentOrganizerAsBinder)) {
+            throw new IllegalArgumentException(
+                    "Can't merge two WCTs from different TaskFragmentOrganizers");
+        }
+        mErrorCallbackToken = mErrorCallbackToken != null
+                ? mErrorCallbackToken
+                : other.mErrorCallbackToken;
     }
 
     /** @hide */
@@ -415,10 +592,26 @@
         return mHierarchyOps;
     }
 
+    /** @hide */
+    @Nullable
+    public IBinder getErrorCallbackToken() {
+        return mErrorCallbackToken;
+    }
+
+    /** @hide */
+    @Nullable
+    public ITaskFragmentOrganizer getTaskFragmentOrganizer() {
+        return mTaskFragmentOrganizer;
+    }
+
     @Override
     @NonNull
     public String toString() {
-        return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
+        return "WindowContainerTransaction {"
+                + " changes = " + mChanges
+                + " hops = " + mHierarchyOps
+                + " errorCallbackToken=" + mErrorCallbackToken
+                + " taskFragmentOrganizer=" + mTaskFragmentOrganizer
                 + " }";
     }
 
@@ -427,6 +620,8 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeMap(mChanges);
         dest.writeList(mHierarchyOps);
+        dest.writeStrongBinder(mErrorCallbackToken);
+        dest.writeStrongInterface(mTaskFragmentOrganizer);
     }
 
     @Override
@@ -705,6 +900,12 @@
         public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
         public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
         public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
+        public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7;
+        public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8;
+        public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
+        public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
+        public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
+        public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -713,75 +914,101 @@
         private final int mType;
 
         // Container we are performing the operation on.
-        private final IBinder mContainer;
+        @Nullable
+        private IBinder mContainer;
 
         // If this is same as mContainer, then only change position, don't reparent.
-        private final IBinder mReparent;
+        @Nullable
+        private IBinder mReparent;
 
         // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
-        private final boolean mToTop;
+        private boolean mToTop;
 
-        final private int[]  mWindowingModes;
-        final private int[] mActivityTypes;
+        @Nullable
+        private int[]  mWindowingModes;
 
-        private final Bundle mLaunchOptions;
+        @Nullable
+        private int[] mActivityTypes;
+
+        @Nullable
+        private Bundle mLaunchOptions;
+
+        @Nullable
+        private Intent mActivityIntent;
+
+        // Used as options for WindowContainerTransaction#createTaskFragment().
+        @Nullable
+        private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+
+        @Nullable
+        private PendingIntent mPendingIntent;
 
         public static HierarchyOp createForReparent(
                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
-                    container, reparent, null, null, toTop, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
+                    .setContainer(container)
+                    .setReparentContainer(reparent)
+                    .setToTop(toTop)
+                    .build();
         }
 
         public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER,
-                    container, container, null, null, toTop, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
+                    .setContainer(container)
+                    .setReparentContainer(container)
+                    .setToTop(toTop)
+                    .build();
         }
 
         public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
                 IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
-                    currentParent, newParent, windowingModes, activityTypes, onTop, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
+                    .setContainer(currentParent)
+                    .setReparentContainer(newParent)
+                    .setWindowingModes(windowingModes)
+                    .setActivityTypes(activityTypes)
+                    .setToTop(onTop)
+                    .build();
         }
 
         public static HierarchyOp createForSetLaunchRoot(IBinder container,
                 int[] windowingModes, int[] activityTypes) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
-                    container, null, windowingModes, activityTypes, false, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)
+                    .setContainer(container)
+                    .setWindowingModes(windowingModes)
+                    .setActivityTypes(activityTypes)
+                    .build();
         }
 
         public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
-                    root1, root2, null, null, false, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
+                    .setContainer(root1)
+                    .setReparentContainer(root2)
+                    .build();
         }
 
         /** Create a hierarchy op for launching a task. */
         public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
             final Bundle fullOptions = options == null ? new Bundle() : options;
             fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
-            return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true,
-                    fullOptions);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+                    .setToTop(true)
+                    .setLaunchOptions(fullOptions)
+                    .build();
         }
 
         /** Create a hierarchy op for setting launch adjacent flag root. */
         public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
                 boolean clearRoot) {
-            return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null,
-                    null, null, clearRoot, null);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT)
+                    .setContainer(container)
+                    .setToTop(clearRoot)
+                    .build();
         }
 
-
-        private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,
-                int[] windowingModes, int[] activityTypes, boolean toTop,
-                @Nullable Bundle launchOptions) {
+        /** Only creates through {@link Builder}. */
+        private HierarchyOp(int type) {
             mType = type;
-            mContainer = container;
-            mReparent = reparent;
-            mWindowingModes = windowingModes != null ?
-                    Arrays.copyOf(windowingModes, windowingModes.length) : null;
-            mActivityTypes = activityTypes != null ?
-                    Arrays.copyOf(activityTypes, activityTypes.length) : null;
-            mToTop = toTop;
-            mLaunchOptions = launchOptions;
         }
 
         public HierarchyOp(@NonNull HierarchyOp copy) {
@@ -792,6 +1019,9 @@
             mWindowingModes = copy.mWindowingModes;
             mActivityTypes = copy.mActivityTypes;
             mLaunchOptions = copy.mLaunchOptions;
+            mActivityIntent = copy.mActivityIntent;
+            mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
+            mPendingIntent = copy.mPendingIntent;
         }
 
         protected HierarchyOp(Parcel in) {
@@ -802,6 +1032,9 @@
             mWindowingModes = in.createIntArray();
             mActivityTypes = in.createIntArray();
             mLaunchOptions = in.readBundle();
+            mActivityIntent = in.readTypedObject(Intent.CREATOR);
+            mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
+            mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
         }
 
         public int getType() {
@@ -844,6 +1077,21 @@
             return mLaunchOptions;
         }
 
+        @Nullable
+        public Intent getActivityIntent() {
+            return mActivityIntent;
+        }
+
+        @Nullable
+        public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
+            return mTaskFragmentCreationOptions;
+        }
+
+        @Nullable
+        public PendingIntent getPendingIntent() {
+            return mPendingIntent;
+        }
+
         @Override
         public String toString() {
             switch (mType) {
@@ -868,6 +1116,19 @@
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
                     return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
                             + "}";
+                case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+                    return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}";
+                case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+                    return "{DeleteTaskFragment: taskFragment=" + mContainer + "}";
+                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+                    return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent="
+                            + mActivityIntent + " options=" + mLaunchOptions + "}";
+                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+                    return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent
+                            + " activity=" + mContainer + "}";
+                case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+                    return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent
+                            + "}";
                 default:
                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                             + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
@@ -884,6 +1145,9 @@
             dest.writeIntArray(mWindowingModes);
             dest.writeIntArray(mActivityTypes);
             dest.writeBundle(mLaunchOptions);
+            dest.writeTypedObject(mActivityIntent, flags);
+            dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
+            dest.writeTypedObject(mPendingIntent, flags);
         }
 
         @Override
@@ -902,5 +1166,105 @@
                 return new HierarchyOp[size];
             }
         };
+
+        private static class Builder {
+
+            private final int mType;
+
+            @Nullable
+            private IBinder mContainer;
+
+            @Nullable
+            private IBinder mReparent;
+
+            private boolean mToTop;
+
+            @Nullable
+            private int[]  mWindowingModes;
+
+            @Nullable
+            private int[] mActivityTypes;
+
+            @Nullable
+            private Bundle mLaunchOptions;
+
+            @Nullable
+            private Intent mActivityIntent;
+
+            @Nullable
+            private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+
+            @Nullable
+            private PendingIntent mPendingIntent;
+
+            Builder(int type) {
+                mType = type;
+            }
+
+            Builder setContainer(@Nullable IBinder container) {
+                mContainer = container;
+                return this;
+            }
+
+            Builder setReparentContainer(@Nullable IBinder reparentContainer) {
+                mReparent = reparentContainer;
+                return this;
+            }
+
+            Builder setToTop(boolean toTop) {
+                mToTop = toTop;
+                return this;
+            }
+
+            Builder setWindowingModes(@Nullable int[] windowingModes) {
+                mWindowingModes = windowingModes;
+                return this;
+            }
+
+            Builder setActivityTypes(@Nullable int[] activityTypes) {
+                mActivityTypes = activityTypes;
+                return this;
+            }
+
+            Builder setLaunchOptions(@Nullable Bundle launchOptions) {
+                mLaunchOptions = launchOptions;
+                return this;
+            }
+
+            Builder setActivityIntent(@Nullable Intent activityIntent) {
+                mActivityIntent = activityIntent;
+                return this;
+            }
+
+            Builder setPendingIntent(@Nullable PendingIntent sender) {
+                mPendingIntent = sender;
+                return this;
+            }
+
+            Builder setTaskFragmentCreationOptions(
+                    @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
+                mTaskFragmentCreationOptions = taskFragmentCreationOptions;
+                return this;
+            }
+
+            HierarchyOp build() {
+                final HierarchyOp hierarchyOp = new HierarchyOp(mType);
+                hierarchyOp.mContainer = mContainer;
+                hierarchyOp.mReparent = mReparent;
+                hierarchyOp.mWindowingModes = mWindowingModes != null
+                        ? Arrays.copyOf(mWindowingModes, mWindowingModes.length)
+                        : null;
+                hierarchyOp.mActivityTypes = mActivityTypes != null
+                        ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
+                        : null;
+                hierarchyOp.mToTop = mToTop;
+                hierarchyOp.mLaunchOptions = mLaunchOptions;
+                hierarchyOp.mActivityIntent = mActivityIntent;
+                hierarchyOp.mPendingIntent = mPendingIntent;
+                hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+
+                return hierarchyOp;
+            }
+        }
     }
 }
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 901625b..5d40085 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,7 +26,6 @@
 import android.content.ContextWrapper;
 import android.content.res.Configuration;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -57,8 +56,14 @@
      *
      * @param base Base {@link Context} for this new instance.
      * @param type Window type to be used with this context.
-     * @param options A bundle used to pass window-related options.
-     *
+     * @param options A bundle used to pass window-related options. For example, on device with
+     *                multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window
+     *                using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options.
+     *                Example usage:
+     *                Bundle options = new Bundle();
+     *                options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+     *                Context windowContext = context.createWindowContext(display, type, options);
+     * @see DisplayAreaInfo#rootDisplayAreaId
      * @hide
      */
     public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
@@ -67,7 +72,7 @@
         mType = type;
         mOptions = options;
         mWindowManager = createWindowContextWindowManager(this);
-        IBinder token = getWindowContextToken();
+        WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
         mController = new WindowContextController(token);
 
         Reference.reachabilityFence(this);
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index d84f571..5aa6233 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -46,7 +47,7 @@
     @VisibleForTesting
     public boolean mAttachedToDisplayArea;
     @NonNull
-    private final IBinder mToken;
+    private final WindowTokenClient mToken;
 
     /**
      * Window Context Controller constructor
@@ -54,14 +55,13 @@
      * @param token The token used to attach to a window manager node. It is usually from
      *              {@link Context#getWindowContextToken()}.
      */
-    public WindowContextController(@NonNull IBinder token) {
-        mToken = token;
-        mWms = WindowManagerGlobal.getWindowManagerService();
+    public WindowContextController(@NonNull WindowTokenClient token) {
+        this(token, WindowManagerGlobal.getWindowManagerService());
     }
 
     /** Used for test only. DO NOT USE it in production code. */
     @VisibleForTesting
-    public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) {
+    public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
         mToken = token;
         mWms = mockWms;
     }
@@ -81,8 +81,15 @@
                     + "a DisplayArea once.");
         }
         try {
-            mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
-                    options);
+            final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
+                    displayId, options);
+            if (configuration != null) {
+                mAttachedToDisplayArea = true;
+                // Send the DisplayArea's configuration to WindowContext directly instead of
+                // waiting for dispatching from WMS.
+                mToken.onConfigurationChanged(configuration, displayId,
+                        false /* shouldReportConfigChange */);
+            }
         }  catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 544d422..e9b8174 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -25,6 +25,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Singleton;
+import android.view.RemoteAnimationAdapter;
 
 /**
  * Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -36,9 +37,16 @@
 
     /**
      * Apply multiple WindowContainer operations at once.
+     *
+     * Note that using this API requires the caller to hold
+     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
+     * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
+     * created by itself.
+     *
      * @param t The transaction to apply.
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
+            conditional = true)
     public void applyTransaction(@NonNull WindowContainerTransaction t) {
         try {
             if (!t.isEmpty()) {
@@ -51,6 +59,12 @@
 
     /**
      * Apply multiple WindowContainer operations at once.
+     *
+     * Note that using this API requires the caller to hold
+     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
+     * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
+     * created by itself.
+     *
      * @param t The transaction to apply.
      * @param callback This transaction will use the synchronization scheme described in
      *        BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
@@ -58,7 +72,8 @@
      * @return An ID for the sync operation which will later be passed to transactionReady callback.
      *         This lets the caller differentiate overlapping sync operations.
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
+            conditional = true)
     public int applySyncTransaction(@NonNull WindowContainerTransaction t,
             @NonNull WindowContainerTransactionCallback callback) {
         try {
@@ -111,6 +126,27 @@
     }
 
     /**
+     * Start a legacy transition.
+     * @param type The type of the transition. This is ignored if a transitionToken is provided.
+     * @param adapter An existing transition to start. If null, a new transition is created.
+     * @param t The set of window operations that are part of this transition.
+     * @return true on success, false if a transition was already running.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+    @NonNull
+    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
+            @NonNull WindowContainerTransactionCallback syncCallback,
+            @NonNull WindowContainerTransaction t) {
+        try {
+            return getWindowOrganizerController().startLegacyTransition(
+                    type, adapter, syncCallback.mInterface, t);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Register an ITransitionPlayer to handle transition animations.
      * @hide
      */
@@ -123,7 +159,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
     IWindowOrganizerController getWindowOrganizerController() {
         return IWindowOrganizerControllerSingleton.get();
     }
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index b8619fb..5171adf 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -36,7 +36,6 @@
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.WindowManagerImpl;
 
-// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
 /**
  * A {@link Service} responsible for showing a non-activity window, such as software keyboards or
  * accessibility overlay windows. This {@link Service} has similar behavior to
@@ -54,6 +53,7 @@
     private final WindowTokenClient mWindowToken = new WindowTokenClient();
     private final WindowContextController mController = new WindowContextController(mWindowToken);
     private WindowManager mWindowManager;
+    private boolean mInitialized;
 
     /**
      * Returns the type of this {@link WindowProviderService}.
@@ -90,6 +90,20 @@
     }
 
     /**
+     * Returns the display ID to launch this {@link WindowProviderService}.
+     *
+     * @hide
+     */
+    @TestApi
+    @SuppressLint({"OnNameExpected"})
+    // Suppress the lint because it is not a callback and users may override this API to provide
+    // display.
+    @NonNull
+    public int getInitialDisplayId() {
+        return DEFAULT_DISPLAY;
+    }
+
+    /**
      * Attaches this WindowProviderService to the {@code windowToken}.
      *
      * @hide
@@ -104,19 +118,22 @@
     public final Context createServiceBaseContext(ActivityThread mainThread,
             LoadedApk packageInfo) {
         final Context context = super.createServiceBaseContext(mainThread, packageInfo);
-        // Always associate with the default display at initialization.
-        final Display defaultDisplay = context.getSystemService(DisplayManager.class)
-                .getDisplay(DEFAULT_DISPLAY);
-        return context.createTokenContext(mWindowToken, defaultDisplay);
+        final Display display = context.getSystemService(DisplayManager.class)
+                .getDisplay(getInitialDisplayId());
+        return context.createTokenContext(mWindowToken, display);
     }
 
-    @CallSuper
+    /** @hide */
     @Override
-    public void onCreate() {
-        super.onCreate();
-        mWindowToken.attachContext(this);
-        mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions());
-        mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+    protected void attachBaseContext(Context newBase) {
+        super.attachBaseContext(newBase);
+        if (!mInitialized) {
+            mWindowToken.attachContext(this);
+            mController.attachToDisplayArea(getWindowType(), getDisplayId(),
+                    getWindowContextOptions());
+            mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+            mInitialized = true;
+        }
     }
 
     @SuppressLint("OnNameExpected")
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 6abf557..284b4b9 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,14 +15,25 @@
  */
 package android.window;
 
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
+
 import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.app.IWindowToken;
 import android.app.ResourcesManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.inputmethodservice.AbstractInputMethodService;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 
@@ -33,11 +44,13 @@
  * {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
  *
  * @see WindowContext
- * @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle)
+ * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
  *
  * @hide
  */
 public class WindowTokenClient extends IWindowToken.Stub {
+    private static final String TAG = WindowTokenClient.class.getSimpleName();
+
     /**
      * Attached {@link Context} for this window token to update configuration and resources.
      * Initialized by {@link #attachContext(Context)}.
@@ -46,12 +59,16 @@
 
     private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
 
+    private final Configuration mConfiguration = new Configuration();
+
+    private boolean mShouldDumpConfigForIme;
+
     /**
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
      * can only attach one {@link Context}.
      * <p>This method must be called before invoking
-     * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
-     * Bundle, boolean)}.<p/>
+     * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
+     * Bundle)}.<p/>
      *
      * @param context context to be attached
      * @throws IllegalStateException if attached context has already existed.
@@ -61,25 +78,85 @@
             throw new IllegalStateException("Context is already attached.");
         }
         mContextRef = new WeakReference<>(context);
+        mConfiguration.setTo(context.getResources().getConfiguration());
+        mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
+                && context instanceof AbstractInputMethodService;
     }
 
+    /**
+     * Called when {@link Configuration} updates from the server side receive.
+     *
+     * @param newConfig the updated {@link Configuration}
+     * @param newDisplayId the updated {@link android.view.Display} ID
+     */
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+        onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+    }
+
+    /**
+     * Called when {@link Configuration} updates from the server side receive.
+     *
+     * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
+     * whether to dispatch configuration update or not.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+            boolean shouldReportConfigChange) {
         final Context context = mContextRef.get();
         if (context == null) {
             return;
         }
-        final int currentDisplayId = context.getDisplayId();
-        final boolean displayChanged = newDisplayId != currentDisplayId;
-        final Configuration config = context.getResources().getConfiguration();
-        final boolean configChanged = config.diff(newConfig) != 0;
-        if (displayChanged || configChanged) {
+        final boolean displayChanged = isDifferentDisplay(context.getDisplayId(), newDisplayId);
+        final boolean shouldUpdateResources = shouldUpdateResources(this, mConfiguration,
+                newConfig, newConfig /* overrideConfig */, displayChanged,
+                null /* configChanged */);
+
+        if (!shouldUpdateResources && mShouldDumpConfigForIme) {
+            Log.d(TAG, "Configuration not dispatch to IME because configuration is up"
+                    + " to date. Current config=" + context.getResources().getConfiguration()
+                    + ", reported config=" + mConfiguration
+                    + ", updated config=" + newConfig);
+        }
+
+        if (shouldUpdateResources) {
             // TODO(ag/9789103): update resource manager logic to track non-activity tokens
             mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
-            if (context instanceof WindowContext) {
+
+            if (shouldReportConfigChange && context instanceof WindowContext) {
+                final WindowContext windowContext = (WindowContext) context;
                 ActivityThread.currentActivityThread().getHandler().post(
-                        () -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
+                        () -> windowContext.dispatchConfigurationChanged(newConfig));
             }
+
+            // Dispatch onConfigurationChanged only if there's a significant public change to
+            // make it compatible with the original behavior.
+            final Configuration[] sizeConfigurations = context.getResources()
+                    .getSizeConfigurations();
+            final SizeConfigurationBuckets buckets = sizeConfigurations != null
+                    ? new SizeConfigurationBuckets(sizeConfigurations) : null;
+            final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
+
+            if (shouldReportConfigChange && diff != 0
+                    && context instanceof WindowProviderService) {
+                final WindowProviderService windowProviderService = (WindowProviderService) context;
+                ActivityThread.currentActivityThread().getHandler().post(
+                        () -> windowProviderService.onConfigurationChanged(newConfig));
+            }
+            freeTextLayoutCachesIfNeeded(diff);
+            if (mShouldDumpConfigForIme) {
+                if (!shouldReportConfigChange) {
+                    Log.d(TAG, "Only apply configuration update to Resources because "
+                            + "shouldReportConfigChange is false.\n" + Debug.getCallers(5));
+                } else if (diff == 0) {
+                    Log.d(TAG, "Configuration not dispatch to IME because configuration has no "
+                            + " public difference with updated config. "
+                            + " Current config=" + context.getResources().getConfiguration()
+                            + ", reported config=" + mConfiguration
+                            + ", updated config=" + newConfig);
+                }
+            }
+            mConfiguration.setTo(newConfig);
         }
         if (displayChanged) {
             context.updateDisplay(newDisplayId);
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 4b4e20f9..6a976ef 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -20,16 +20,23 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 
+import libcore.util.EmptyArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -39,7 +46,25 @@
  * Collection of utilities for accessibility service.
  */
 public final class AccessibilityUtils {
-    private AccessibilityUtils() {}
+    private AccessibilityUtils() {
+    }
+
+    /** @hide */
+    @IntDef(value = {
+            NONE,
+            TEXT,
+            PARCELABLE_SPAN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface A11yTextChangeType {
+    }
+
+    /** Specifies no content has been changed for accessibility. */
+    public static final int NONE = 0;
+    /** Specifies some readable sequence has been changed. */
+    public static final int TEXT = 1;
+    /** Specifies some parcelable spans has been changed. */
+    public static final int PARCELABLE_SPAN = 2;
 
     /**
      * Returns the set of enabled accessibility services for userId. If there are no
@@ -168,4 +193,55 @@
                 Settings.Secure.USER_SETUP_COMPLETE, /* def= */ 0, UserHandle.USER_CURRENT)
                 != /* false */ 0;
     }
+
+    /**
+     * Returns the text change type for accessibility. It only cares about readable sequence changes
+     * or {@link ParcelableSpan} changes which are able to pass via IPC.
+     *
+     * @param before The CharSequence before changing
+     * @param after  The CharSequence after changing
+     * @return Returns {@code TEXT} for readable sequence changes or {@code PARCELABLE_SPAN} for
+     * ParcelableSpan changes. Otherwise, returns {@code NONE}.
+     */
+    @A11yTextChangeType
+    public static int textOrSpanChanged(CharSequence before, CharSequence after) {
+        if (!TextUtils.equals(before, after)) {
+            return TEXT;
+        }
+        if (before instanceof Spanned || after instanceof Spanned) {
+            if (!parcelableSpansEquals(before, after)) {
+                return PARCELABLE_SPAN;
+            }
+        }
+        return NONE;
+    }
+
+    private static boolean parcelableSpansEquals(CharSequence before, CharSequence after) {
+        Object[] spansA = EmptyArray.OBJECT;
+        Object[] spansB = EmptyArray.OBJECT;
+        Spanned a = null;
+        Spanned b = null;
+        if (before instanceof Spanned) {
+            a = (Spanned) before;
+            spansA = a.getSpans(0, a.length(), ParcelableSpan.class);
+        }
+        if (after instanceof Spanned) {
+            b = (Spanned) after;
+            spansB = b.getSpans(0, b.length(), ParcelableSpan.class);
+        }
+        if (spansA.length != spansB.length) {
+            return false;
+        }
+        for (int i = 0; i < spansA.length; ++i) {
+            final Object thisSpan = spansA[i];
+            final Object otherSpan = spansB[i];
+            if ((thisSpan.getClass() != otherSpan.getClass())
+                    || (a.getSpanStart(thisSpan) != b.getSpanStart(otherSpan))
+                    || (a.getSpanEnd(thisSpan) != b.getSpanEnd(otherSpan))
+                    || (a.getSpanFlags(thisSpan) != b.getSpanFlags(otherSpan))) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6bf4645..1d13b73f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -145,7 +145,7 @@
 
     void noteBleScanStarted(in WorkSource ws, boolean isUnoptimized);
     void noteBleScanStopped(in WorkSource ws, boolean isUnoptimized);
-    void noteResetBleScan();
+    void noteBleScanReset();
     void noteBleScanResults(in WorkSource ws, int numNewResults);
 
     /** {@hide} */
diff --git a/core/java/com/android/internal/content/om/OWNERS b/core/java/com/android/internal/content/om/OWNERS
new file mode 100644
index 0000000..44fd9fd
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 568631
+include /core/java/android/content/om/OWNERS
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index 3958b9e..1933c54 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -16,15 +16,9 @@
 
 package com.android.internal.inputmethod;
 
-import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.os.RemoteException;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.internal.view.InputBindResult;
-
-import java.util.List;
 import java.util.function.BooleanSupplier;
 import java.util.function.IntSupplier;
 import java.util.function.Supplier;
@@ -41,34 +35,6 @@
     }
 
     /**
-     * A utility method using given {@link IInputBindResultResultCallback} to callback the
-     * {@link InputBindResult}.
-     *
-     * @param callback {@link IInputBindResultResultCallback} to be called back.
-     * @param resultSupplier the supplier from which {@link InputBindResult} is provided.
-     */
-    @AnyThread
-    public static void onResult(@NonNull IInputBindResultResultCallback callback,
-            @NonNull Supplier<InputBindResult> resultSupplier) {
-        InputBindResult result = null;
-        Throwable exception = null;
-
-        try {
-            result = resultSupplier.get();
-        } catch (Throwable throwable) {
-            exception = throwable;
-        }
-
-        try {
-            if (exception != null) {
-                callback.onError(ThrowableHolder.of(exception));
-                return;
-            }
-            callback.onResult(result);
-        } catch (RemoteException ignored) { }
-    }
-
-    /**
      * A utility method using given {@link IBooleanResultCallback} to callback the result.
      *
      * @param callback {@link IBooleanResultCallback} to be called back.
@@ -95,87 +61,6 @@
     }
 
     /**
-     * A utility method using given {@link IInputMethodSubtypeResultCallback} to callback the
-     * result.
-     *
-     * @param callback {@link IInputMethodSubtypeResultCallback} to be called back.
-     * @param resultSupplier the supplier from which the result is provided.
-     */
-    public static void onResult(@NonNull IInputMethodSubtypeResultCallback callback,
-            @NonNull Supplier<InputMethodSubtype> resultSupplier) {
-        InputMethodSubtype result = null;
-        Throwable exception = null;
-
-        try {
-            result = resultSupplier.get();
-        } catch (Throwable throwable) {
-            exception = throwable;
-        }
-
-        try {
-            if (exception != null) {
-                callback.onError(ThrowableHolder.of(exception));
-                return;
-            }
-            callback.onResult(result);
-        } catch (RemoteException ignored) { }
-    }
-
-    /**
-     * A utility method using given {@link IInputMethodSubtypeListResultCallback} to callback the
-     * result.
-     *
-     * @param callback {@link IInputMethodSubtypeListResultCallback} to be called back.
-     * @param resultSupplier the supplier from which the result is provided.
-     */
-    public static void onResult(@NonNull IInputMethodSubtypeListResultCallback callback,
-            @NonNull Supplier<List<InputMethodSubtype>> resultSupplier) {
-        List<InputMethodSubtype> result = null;
-        Throwable exception = null;
-
-        try {
-            result = resultSupplier.get();
-        } catch (Throwable throwable) {
-            exception = throwable;
-        }
-
-        try {
-            if (exception != null) {
-                callback.onError(ThrowableHolder.of(exception));
-                return;
-            }
-            callback.onResult(result);
-        } catch (RemoteException ignored) { }
-    }
-
-    /**
-     * A utility method using given {@link IInputMethodInfoListResultCallback} to callback the
-     * result.
-     *
-     * @param callback {@link IInputMethodInfoListResultCallback} to be called back.
-     * @param resultSupplier the supplier from which the result is provided.
-     */
-    public static void onResult(@NonNull IInputMethodInfoListResultCallback callback,
-            @NonNull Supplier<List<InputMethodInfo>> resultSupplier) {
-        List<InputMethodInfo> result = null;
-        Throwable exception = null;
-
-        try {
-            result = resultSupplier.get();
-        } catch (Throwable throwable) {
-            exception = throwable;
-        }
-
-        try {
-            if (exception != null) {
-                callback.onError(ThrowableHolder.of(exception));
-                return;
-            }
-            callback.onResult(result);
-        } catch (RemoteException ignored) { }
-    }
-
-    /**
      * A utility method using given {@link IIntResultCallback} to callback the result.
      *
      * @param callback {@link IIntResultCallback} to be called back.
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index ba3a343..b419e26 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -544,6 +544,24 @@
     }
 
     /**
+     * Await the result by the {@link Completable.Boolean}, and log it if there is no result after
+     * given timeout.
+     *
+     * @return the result once {@link ValueBase#onComplete()}
+     */
+    @AnyThread
+    public static boolean getResultOrFalse(@NonNull Completable.Boolean value, String tag,
+            @NonNull String methodName, @Nullable CancellationGroup cancellationGroup,
+            int maxWaitTime) {
+        final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup);
+        if (value.hasValue()) {
+            return value.getValue();
+        }
+        logInternal(tag, methodName, timedOut, maxWaitTime, 0);
+        return false;
+    }
+
+    /**
      * Await the result by the {@link Completable.Int}, and log it if there is no result after
      * given timeout.
      *
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
new file mode 100644
index 0000000..410e68b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE;
+import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
+
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.method.KeyListener;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.widget.TextView;
+
+/**
+ * Base class for an editable InputConnection instance. This is created by {@link TextView} or
+ * {@link android.widget.EditText}.
+ */
+public final class EditableInputConnection extends BaseInputConnection
+        implements DumpableInputConnection {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "EditableInputConnection";
+
+    private final TextView mTextView;
+
+    // Keeps track of nested begin/end batch edit to ensure this connection always has a
+    // balanced impact on its associated TextView.
+    // A negative value means that this connection has been finished by the InputMethodManager.
+    private int mBatchEditNesting;
+
+    public EditableInputConnection(TextView textview) {
+        super(textview, true);
+        mTextView = textview;
+    }
+
+    @Override
+    public Editable getEditable() {
+        TextView tv = mTextView;
+        if (tv != null) {
+            return tv.getEditableText();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean beginBatchEdit() {
+        synchronized (this) {
+            if (mBatchEditNesting >= 0) {
+                mTextView.beginBatchEdit();
+                mBatchEditNesting++;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean endBatchEdit() {
+        synchronized (this) {
+            if (mBatchEditNesting > 0) {
+                // When the connection is reset by the InputMethodManager and reportFinish
+                // is called, some endBatchEdit calls may still be asynchronously received from the
+                // IME. Do not take these into account, thus ensuring that this IC's final
+                // contribution to mTextView's nested batch edit count is zero.
+                mTextView.endBatchEdit();
+                mBatchEditNesting--;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void endComposingRegionEditInternal() {
+        // The ContentCapture service is interested in Composing-state changes.
+        mTextView.notifyContentCaptureTextChanged();
+    }
+
+    @Override
+    public void closeConnection() {
+        super.closeConnection();
+        synchronized (this) {
+            while (mBatchEditNesting > 0) {
+                endBatchEdit();
+            }
+            // Will prevent any further calls to begin or endBatchEdit
+            mBatchEditNesting = -1;
+        }
+    }
+
+    @Override
+    public boolean clearMetaKeyStates(int states) {
+        final Editable content = getEditable();
+        if (content == null) return false;
+        KeyListener kl = mTextView.getKeyListener();
+        if (kl != null) {
+            try {
+                kl.clearMetaKeyState(mTextView, content, states);
+            } catch (AbstractMethodError e) {
+                // This is an old listener that doesn't implement the
+                // new method.
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean commitCompletion(CompletionInfo text) {
+        if (DEBUG) Log.v(TAG, "commitCompletion " + text);
+        mTextView.beginBatchEdit();
+        mTextView.onCommitCompletion(text);
+        mTextView.endBatchEdit();
+        return true;
+    }
+
+    /**
+     * Calls the {@link TextView#onCommitCorrection} method of the associated TextView.
+     */
+    @Override
+    public boolean commitCorrection(CorrectionInfo correctionInfo) {
+        if (DEBUG) Log.v(TAG, "commitCorrection" + correctionInfo);
+        mTextView.beginBatchEdit();
+        mTextView.onCommitCorrection(correctionInfo);
+        mTextView.endBatchEdit();
+        return true;
+    }
+
+    @Override
+    public boolean performEditorAction(int actionCode) {
+        if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode);
+        mTextView.onEditorAction(actionCode);
+        return true;
+    }
+
+    @Override
+    public boolean performContextMenuAction(int id) {
+        if (DEBUG) Log.v(TAG, "performContextMenuAction " + id);
+        mTextView.beginBatchEdit();
+        mTextView.onTextContextMenuItem(id);
+        mTextView.endBatchEdit();
+        return true;
+    }
+
+    @Override
+    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        if (mTextView != null) {
+            ExtractedText et = new ExtractedText();
+            if (mTextView.extractText(request, et)) {
+                if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) {
+                    mTextView.setExtracting(request);
+                }
+                return et;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean performSpellCheck() {
+        mTextView.onPerformSpellCheck();
+        return true;
+    }
+
+    @Override
+    public boolean performPrivateCommand(String action, Bundle data) {
+        mTextView.onPrivateIMECommand(action, data);
+        return true;
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        if (mTextView == null) {
+            return super.commitText(text, newCursorPosition);
+        }
+        mTextView.resetErrorChangedFlag();
+        boolean success = super.commitText(text, newCursorPosition);
+        mTextView.hideErrorIfUnchanged();
+
+        return success;
+    }
+
+    @Override
+    public boolean requestCursorUpdates(int cursorUpdateMode) {
+        if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
+
+        // It is possible that any other bit is used as a valid flag in a future release.
+        // We should reject the entire request in such a case.
+        final int knownFlagMask = InputConnection.CURSOR_UPDATE_IMMEDIATE
+                | InputConnection.CURSOR_UPDATE_MONITOR;
+        final int unknownFlags = cursorUpdateMode & ~knownFlagMask;
+        if (unknownFlags != 0) {
+            if (DEBUG) {
+                Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags. "
+                        + "cursorUpdateMode=" + cursorUpdateMode + " unknownFlags=" + unknownFlags);
+            }
+            return false;
+        }
+
+        if (mIMM == null) {
+            // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled.
+            // TODO: Return some notification code rather than false to indicate method that
+            // CursorAnchorInfo is temporarily unavailable.
+            return false;
+        }
+        mIMM.setUpdateCursorAnchorInfoMode(cursorUpdateMode);
+        if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) {
+            if (mTextView == null) {
+                // In this case, FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is silently ignored.
+                // TODO: Return some notification code for the input method that indicates
+                // FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is ignored.
+            } else if (mTextView.isInLayout()) {
+                // In this case, the view hierarchy is currently undergoing a layout pass.
+                // IMM#updateCursorAnchorInfo is supposed to be called soon after the layout
+                // pass is finished.
+            } else {
+                // This will schedule a layout pass of the view tree, and the layout event
+                // eventually triggers IMM#updateCursorAnchorInfo.
+                mTextView.requestLayout();
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
+        if (mTextView == null) {
+            return super.setImeConsumesInput(imeConsumesInput);
+        }
+        mTextView.setImeConsumesInput(imeConsumesInput);
+        return true;
+    }
+
+    @Override
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        CharSequence editableText = mTextView.getText();
+        CharSequence selectedText = getSelectedText(0 /* flags */);
+        if (InputConnectionProtoDumper.DUMP_TEXT) {
+            if (editableText != null) {
+                proto.write(EDITABLE_TEXT, editableText.toString());
+            }
+            if (selectedText != null) {
+                proto.write(SELECTED_TEXT, selectedText.toString());
+            }
+        }
+        final Editable content = getEditable();
+        if (content != null) {
+            int start = Selection.getSelectionStart(content);
+            int end = Selection.getSelectionEnd(content);
+            proto.write(SELECTED_TEXT_START, start);
+            proto.write(SELECTED_TEXT_END, end);
+        }
+        proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0));
+        proto.end(token);
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
deleted file mode 100644
index 2691984..0000000
--- a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.internal.inputmethod;
-
-import com.android.internal.view.InputBindResult;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputBindResultResultCallback {
-    void onResult(in InputBindResult result);
-    void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
new file mode 100644
index 0000000..977f9a5
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputContentInfo;
+
+import com.android.internal.view.IInputContext;
+
+import java.util.Objects;
+
+/**
+ * A stateless wrapper of {@link com.android.internal.view.IInputContext} to encapsulate boilerplate
+ * code around {@link Completable} and {@link RemoteException}.
+ */
+public final class IInputContextInvoker {
+
+    @NonNull
+    private final IInputContext mIInputContext;
+
+    private IInputContextInvoker(@NonNull IInputContext inputContext) {
+        mIInputContext = inputContext;
+    }
+
+    /**
+     * Creates a new instance of {@link IInputContextInvoker} for the given {@link IInputContext}.
+     *
+     * @param inputContext {@link IInputContext} to be wrapped.
+     * @return A new instance of {@link IInputContextInvoker}.
+     */
+    public static IInputContextInvoker create(@NonNull IInputContext inputContext) {
+        Objects.requireNonNull(inputContext);
+        return new IInputContextInvoker(inputContext);
+    }
+
+    /**
+     * Invokes {@link IInputContext#getTextAfterCursor(int, int,
+     * com.android.internal.inputmethod.ICharSequenceResultCallback)}.
+     *
+     * @param length {@code length} parameter to be passed.
+     * @param flags {@code flags} parameter to be passed.
+     * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.CharSequence getTextAfterCursor(int length, int flags) {
+        final Completable.CharSequence value = Completable.createCharSequence();
+        try {
+            mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#getTextBeforeCursor(int, int, ICharSequenceResultCallback)}.
+     *
+     * @param length {@code length} parameter to be passed.
+     * @param flags {@code flags} parameter to be passed.
+     * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.CharSequence getTextBeforeCursor(int length, int flags) {
+        final Completable.CharSequence value = Completable.createCharSequence();
+        try {
+            mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
+     *
+     * @param flags {@code flags} parameter to be passed.
+     * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.CharSequence getSelectedText(int flags) {
+        final Completable.CharSequence value = Completable.createCharSequence();
+        try {
+            mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes
+     * {@link IInputContext#getSurroundingText(int, int, int, ISurroundingTextResultCallback)}.
+     *
+     * @param beforeLength {@code beforeLength} parameter to be passed.
+     * @param afterLength {@code afterLength} parameter to be passed.
+     * @param flags {@code flags} parameter to be passed.
+     * @return {@link Completable.SurroundingText} that can be used to retrieve the invocation
+     *         result. {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.SurroundingText getSurroundingText(int beforeLength, int afterLength,
+            int flags) {
+        final Completable.SurroundingText value = Completable.createSurroundingText();
+        try {
+            mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+                    ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
+     *
+     * @param reqModes {@code reqModes} parameter to be passed.
+     * @return {@link Completable.Int} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.Int getCursorCapsMode(int reqModes) {
+        final Completable.Int value = Completable.createInt();
+        try {
+            mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#getExtractedText(ExtractedTextRequest, int,
+     * IExtractedTextResultCallback)}.
+     *
+     * @param request {@code request} parameter to be passed.
+     * @param flags {@code flags} parameter to be passed.
+     * @return {@link Completable.ExtractedText} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        final Completable.ExtractedText value = Completable.createExtractedText();
+        try {
+            mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#commitText(CharSequence, int)}.
+     *
+     * @param text {@code text} parameter to be passed.
+     * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        try {
+            mIInputContext.commitText(text, newCursorPosition);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#commitCompletion(CompletionInfo)}.
+     *
+     * @param text {@code text} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean commitCompletion(CompletionInfo text) {
+        try {
+            mIInputContext.commitCompletion(text);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#commitCorrection(CorrectionInfo)}.
+     *
+     * @param correctionInfo {@code correctionInfo} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean commitCorrection(CorrectionInfo correctionInfo) {
+        try {
+            mIInputContext.commitCorrection(correctionInfo);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#setSelection(int, int)}.
+     *
+     * @param start {@code start} parameter to be passed.
+     * @param end {@code start} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setSelection(int start, int end) {
+        try {
+            mIInputContext.setSelection(start, end);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#performEditorAction(int)}.
+     *
+     * @param actionCode {@code start} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean performEditorAction(int actionCode) {
+        try {
+            mIInputContext.performEditorAction(actionCode);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#performContextMenuAction(id)}.
+     *
+     * @param id {@code id} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean performContextMenuAction(int id) {
+        try {
+            mIInputContext.performContextMenuAction(id);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#setComposingRegion(int, int)}.
+     *
+     * @param start {@code id} parameter to be passed.
+     * @param end {@code id} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setComposingRegion(int start, int end) {
+        try {
+            mIInputContext.setComposingRegion(start, end);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#setComposingText(CharSequence, int)}.
+     *
+     * @param text {@code text} parameter to be passed.
+     * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        try {
+            mIInputContext.setComposingText(text, newCursorPosition);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#finishComposingText()}.
+     *
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean finishComposingText() {
+        try {
+            mIInputContext.finishComposingText();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#beginBatchEdit()}.
+     *
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean beginBatchEdit() {
+        try {
+            mIInputContext.beginBatchEdit();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#endBatchEdit()}.
+     *
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean endBatchEdit() {
+        try {
+            mIInputContext.endBatchEdit();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#sendKeyEvent(KeyEvent)}.
+     *
+     * @param event {@code event} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean sendKeyEvent(KeyEvent event) {
+        try {
+            mIInputContext.sendKeyEvent(event);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#clearMetaKeyStates(int)}.
+     *
+     * @param states {@code states} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean clearMetaKeyStates(int states) {
+        try {
+            mIInputContext.clearMetaKeyStates(states);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#deleteSurroundingText(int, int)}.
+     *
+     * @param beforeLength {@code beforeLength} parameter to be passed.
+     * @param afterLength {@code afterLength} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        try {
+            mIInputContext.deleteSurroundingText(beforeLength, afterLength);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(int, int)}.
+     *
+     * @param beforeLength {@code beforeLength} parameter to be passed.
+     * @param afterLength {@code afterLength} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+        try {
+            mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#performSpellCheck()}.
+     *
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean performSpellCheck() {
+        try {
+            mIInputContext.performSpellCheck();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#performPrivateCommand(String, Bundle)}.
+     *
+     * @param action {@code action} parameter to be passed.
+     * @param data {@code data} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean performPrivateCommand(String action, Bundle data) {
+        try {
+            mIInputContext.performPrivateCommand(action, data);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
+     *
+     * @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
+     * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.Boolean requestCursorUpdates(int cursorUpdateMode) {
+        final Completable.Boolean value = Completable.createBoolean();
+        try {
+            mIInputContext.requestCursorUpdates(cursorUpdateMode, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes
+     * {@link IInputContext#commitContent(InputContentInfo, int, Bundle, IIntResultCallback)}.
+     *
+     * @param inputContentInfo {@code inputContentInfo} parameter to be passed.
+     * @param flags {@code flags} parameter to be passed.
+     * @param opts {@code opts} parameter to be passed.
+     * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
+     *         {@link RemoteException} will be treated as an error.
+     */
+    @AnyThread
+    @NonNull
+    public Completable.Boolean commitContent(InputContentInfo inputContentInfo, int flags,
+            Bundle opts) {
+        final Completable.Boolean value = Completable.createBoolean();
+        try {
+            mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
+        } catch (RemoteException e) {
+            value.onError(ThrowableHolder.of(e));
+        }
+        return value;
+    }
+
+    /**
+     * Invokes {@link IInputContext#setImeConsumesInput(boolean)}.
+     *
+     * @param imeConsumesInput {@code imeConsumesInput} parameter to be passed.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
+        try {
+            mIInputContext.setImeConsumesInput(imeConsumesInput);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl
deleted file mode 100644
index 0dfd5da..0000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.internal.inputmethod;
-
-import android.view.inputmethod.InputMethodInfo;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodInfoListResultCallback {
-    void onResult(in List<InputMethodInfo> result);
-    void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl
deleted file mode 100644
index 619c87e..0000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.internal.inputmethod;
-
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodSubtypeListResultCallback {
-    void onResult(in List<InputMethodSubtype> result);
-    void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl
deleted file mode 100644
index 66c0902..0000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.internal.inputmethod;
-
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodSubtypeResultCallback {
-    void onResult(in InputMethodSubtype result);
-    void onError(in ThrowableHolder exception);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
deleted file mode 100644
index 2f782ba..0000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
+++ /dev/null
@@ -1,26 +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.internal.inputmethod;
-
-import android.os.IBinder;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-
-oneway interface IMultiClientInputMethod {
-    void initialize(in IMultiClientInputMethodPrivilegedOperations privOps);
-    void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
-    void removeClient(int clientId);
-}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
deleted file mode 100644
index b5f2147..0000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.view.InputChannel;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-
-/**
- * Defines priviledged operations that only the current MSIMS is allowed to call.
- * Actual operations are implemented and handled by MultiClientInputMethodManagerService.
- */
-interface IMultiClientInputMethodPrivilegedOperations {
-    IBinder createInputMethodWindowToken(int displayId);
-    void deleteInputMethodWindowToken(IBinder token);
-    void acceptClient(int clientId, in IInputMethodSession session,
-            in IMultiClientInputMethodSession multiClientSession, in InputChannel writeChannel);
-    void reportImeWindowTarget(int clientId, int targetWindowHandle, in IBinder imeWindowToken);
-    boolean isUidAllowedOnDisplay(int displayId, int uid);
-    void setActive(int clientId, boolean active);
-}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
deleted file mode 100644
index b40e82c..0000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
+++ /dev/null
@@ -1,29 +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.internal.inputmethod;
-
-import android.os.ResultReceiver;
-import android.view.inputmethod.EditorInfo;
-import com.android.internal.view.IInputContext;
-
-oneway interface IMultiClientInputMethodSession {
-    void startInputOrWindowGainedFocus(
-            in IInputContext inputContext, int missingMethods, in EditorInfo attribute,
-            int controlFlags, int softInputMode, int targetWindowHandle);
-    void showSoftInput(int flags, in ResultReceiver resultReceiver);
-    void hideSoftInput(int flags, in ResultReceiver resultReceiver);
-}
diff --git a/core/java/com/android/internal/inputmethod/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
new file mode 100644
index 0000000..8b21b7e
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -0,0 +1,222 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ * An abstract class that declares the methods for ime trace related operations - enable trace,
+ * schedule trace and add new trace to buffer. Both the client and server side classes can use
+ * it by getting an implementation through {@link ImeTracing#getInstance()}.
+ */
+public abstract class ImeTracing {
+
+    static final String TAG = "imeTracing";
+    public static final String PROTO_ARG = "--proto-com-android-imetracing";
+
+    /* Constants describing the component type that triggered a dump. */
+    public static final int IME_TRACING_FROM_CLIENT = 0;
+    public static final int IME_TRACING_FROM_IMS = 1;
+    public static final int IME_TRACING_FROM_IMMS = 2;
+
+    private static ImeTracing sInstance;
+    static boolean sEnabled = false;
+    IInputMethodManager mService;
+
+    protected boolean mDumpInProgress;
+    protected final Object mDumpInProgressLock = new Object();
+
+    ImeTracing() throws ServiceNotFoundException {
+        mService = IInputMethodManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+    }
+
+    /**
+     * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class
+     * and an instance of {@link ImeTracingClientImpl} when called from a client side class.
+     * Useful to schedule a dump for next frame or save a dump when certain methods are called.
+     *
+     * @return Instance of one of the children classes of {@link ImeTracing}
+     */
+    public static ImeTracing getInstance() {
+        if (sInstance == null) {
+            try {
+                sInstance = isSystemProcess()
+                        ? new ImeTracingServerImpl() : new ImeTracingClientImpl();
+            } catch (RemoteException | ServiceNotFoundException e) {
+                Log.e(TAG, "Exception while creating ImeTracing instance", e);
+            }
+        }
+        return sInstance;
+    }
+
+    /**
+     * Transmits the information from client or InputMethodService side to the server, in order to
+     * be stored persistently to the current IME tracing dump.
+     *
+     * @param protoDump client or service side information to be stored by the server
+     * @param source where the information is coming from, refer to {@see #IME_TRACING_FROM_CLIENT}
+     * and {@see #IME_TRACING_FROM_IMS}
+     * @param where
+     */
+    public void sendToService(byte[] protoDump, int source, String where) throws RemoteException {
+        mService.startProtoDump(protoDump, source, where);
+    }
+
+    /**
+     * Calling {@link IInputMethodManager#startImeTrace()}} to capture IME trace.
+     */
+    public final void startImeTrace() {
+        try {
+            mService.startImeTrace();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not start ime trace." + e);
+        }
+    }
+
+    /**
+     * Calling {@link IInputMethodManager#stopImeTrace()} to stop IME trace.
+     */
+    public final void stopImeTrace() {
+        try {
+            mService.stopImeTrace();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not stop ime trace." + e);
+        }
+    }
+
+    /**
+     * @param proto dump to be added to the buffer
+     */
+    public abstract void addToBuffer(ProtoOutputStream proto, int source);
+
+    /**
+     * Starts a proto dump of the client side information.
+     *
+     * @param where Place where the trace was triggered.
+     * @param immInstance The {@link InputMethodManager} instance to dump.
+     * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
+     */
+    public abstract void triggerClientDump(String where, InputMethodManager immInstance,
+            @Nullable byte[] icProto);
+
+    /**
+     * A delegate for {@link #triggerServiceDump(String, ServiceDumper, byte[])}.
+     */
+    @FunctionalInterface
+    public interface ServiceDumper {
+        /**
+         * Dumps internal data into {@link ProtoOutputStream}.
+         *
+         * @param proto {@link ProtoOutputStream} to be dumped into.
+         * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto
+         *                format.
+         */
+        void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto);
+    }
+
+    /**
+     * Starts a proto dump of the currently connected InputMethodService information.
+     *
+     * @param where Place where the trace was triggered.
+     * @param dumper {@link ServiceDumper} to be used to dump
+     *               {@link android.inputmethodservice.InputMethodService}.
+     * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
+     */
+    public abstract void triggerServiceDump(String where, ServiceDumper dumper,
+            @Nullable byte[] icProto);
+
+    /**
+     * Starts a proto dump of the InputMethodManagerService information.
+     *
+     * @param where Place where the trace was triggered.
+     */
+    public abstract void triggerManagerServiceDump(String where);
+
+    /**
+     * Being called while taking a bugreport so that tracing files can be included in the bugreport
+     * when the IME tracing is running.  Does nothing otherwise.
+     *
+     * @param pw Print writer
+     */
+    public void saveForBugreport(@Nullable PrintWriter pw) {
+        // does nothing by default.
+    }
+
+    /**
+     * Sets whether ime tracing is enabled.
+     *
+     * @param enabled Tells whether ime tracing should be enabled or disabled.
+     */
+    public void setEnabled(boolean enabled) {
+        sEnabled = enabled;
+    }
+
+    /**
+     * @return {@code true} if dumping is enabled, {@code false} otherwise.
+     */
+    public boolean isEnabled() {
+        return sEnabled;
+    }
+
+    /**
+     * @return {@code true} if tracing is available, {@code false} otherwise.
+     */
+    public boolean isAvailable() {
+        return mService != null;
+    }
+
+    /**
+     * Starts a new IME trace if one is not already started.
+     *
+     * @param pw Print writer
+     */
+    public abstract void startTrace(@Nullable PrintWriter pw);
+
+    /**
+     * Stops the IME trace if one was previously started and writes the current buffers to disk.
+     *
+     * @param pw Print writer
+     */
+    public abstract void stopTrace(@Nullable PrintWriter pw);
+
+    private static boolean isSystemProcess() {
+        return ActivityThread.isSystem();
+    }
+
+    protected void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Log.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
new file mode 100644
index 0000000..31d278b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
@@ -0,0 +1,103 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodManager;
+
+import java.io.PrintWriter;
+
+/**
+ * An implementation of {@link ImeTracing} for non system_server processes.
+ */
+class ImeTracingClientImpl extends ImeTracing {
+    ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
+        sEnabled = mService.isImeTraceEnabled();
+    }
+
+    @Override
+    public void addToBuffer(ProtoOutputStream proto, int source) {
+    }
+
+    @Override
+    public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
+            @Nullable byte[] icProto) {
+        if (!isEnabled() || !isAvailable()) {
+            return;
+        }
+
+        synchronized (mDumpInProgressLock) {
+            if (mDumpInProgress) {
+                return;
+            }
+            mDumpInProgress = true;
+        }
+
+        try {
+            ProtoOutputStream proto = new ProtoOutputStream();
+            immInstance.dumpDebug(proto, icProto);
+            sendToService(proto.getBytes(), IME_TRACING_FROM_CLIENT, where);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while sending ime-related client dump to server", e);
+        } finally {
+            mDumpInProgress = false;
+        }
+    }
+
+    @Override
+    public void triggerServiceDump(String where, @NonNull ServiceDumper dumper,
+            @Nullable byte[] icProto) {
+        if (!isEnabled() || !isAvailable()) {
+            return;
+        }
+
+        synchronized (mDumpInProgressLock) {
+            if (mDumpInProgress) {
+                return;
+            }
+            mDumpInProgress = true;
+        }
+
+        try {
+            ProtoOutputStream proto = new ProtoOutputStream();
+            dumper.dumpToProto(proto, icProto);
+            sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while sending ime-related service dump to server", e);
+        } finally {
+            mDumpInProgress = false;
+        }
+    }
+
+    @Override
+    public void triggerManagerServiceDump(String where) {
+        // Intentionally left empty, this is implemented in ImeTracingServerImpl
+    }
+
+    @Override
+    public void startTrace(PrintWriter pw) {
+    }
+
+    @Override
+    public void stopTrace(PrintWriter pw) {
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
new file mode 100644
index 0000000..d452e60
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
@@ -0,0 +1,237 @@
+/*
+ * 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.inputmethod;
+
+import static android.os.Build.IS_USER;
+
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
+import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodManagerServiceTraceFileProto;
+import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceFileProto;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * An implementation of {@link ImeTracing} for the system_server process.
+ */
+class ImeTracingServerImpl extends ImeTracing {
+    private static final String TRACE_DIRNAME = "/data/misc/wmtrace/";
+    private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.pb";
+    private static final String TRACE_FILENAME_IMS = "ime_trace_service.pb";
+    private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.pb";
+    private static final int BUFFER_CAPACITY = 4096 * 1024;
+
+    // Needed for winscope to auto-detect the dump type. Explained further in
+    // core.proto.android.view.inputmethod.inputmethodeditortrace.proto.
+    // This magic number corresponds to InputMethodClientsTraceFileProto.
+    private static final long MAGIC_NUMBER_CLIENTS_VALUE =
+            ((long) InputMethodClientsTraceFileProto.MAGIC_NUMBER_H << 32)
+                | InputMethodClientsTraceFileProto.MAGIC_NUMBER_L;
+    // This magic number corresponds to InputMethodServiceTraceFileProto.
+    private static final long MAGIC_NUMBER_IMS_VALUE =
+            ((long) InputMethodServiceTraceFileProto.MAGIC_NUMBER_H << 32)
+                | InputMethodServiceTraceFileProto.MAGIC_NUMBER_L;
+    // This magic number corresponds to InputMethodManagerServiceTraceFileProto.
+    private static final long MAGIC_NUMBER_IMMS_VALUE =
+            ((long) InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_H << 32)
+                | InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_L;
+
+    private final TraceBuffer mBufferClients;
+    private final File mTraceFileClients;
+    private final TraceBuffer mBufferIms;
+    private final File mTraceFileIms;
+    private final TraceBuffer mBufferImms;
+    private final File mTraceFileImms;
+
+    private final Object mEnabledLock = new Object();
+
+    ImeTracingServerImpl() throws ServiceNotFoundException {
+        mBufferClients = new TraceBuffer<>(BUFFER_CAPACITY);
+        mTraceFileClients = new File(TRACE_DIRNAME + TRACE_FILENAME_CLIENTS);
+        mBufferIms = new TraceBuffer<>(BUFFER_CAPACITY);
+        mTraceFileIms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMS);
+        mBufferImms = new TraceBuffer<>(BUFFER_CAPACITY);
+        mTraceFileImms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMMS);
+    }
+
+    /**
+     * The provided dump is added to the corresponding dump buffer:
+     * {@link ImeTracingServerImpl#mBufferClients} or {@link ImeTracingServerImpl#mBufferIms}.
+     *
+     * @param proto dump to be added to the buffer
+     */
+    @Override
+    public void addToBuffer(ProtoOutputStream proto, int source) {
+        if (isAvailable() && isEnabled()) {
+            switch (source) {
+                case IME_TRACING_FROM_CLIENT:
+                    mBufferClients.add(proto);
+                    return;
+                case IME_TRACING_FROM_IMS:
+                    mBufferIms.add(proto);
+                    return;
+                case IME_TRACING_FROM_IMMS:
+                    mBufferImms.add(proto);
+                    return;
+                default:
+                    // Source not recognised.
+                    Log.w(TAG, "Request to add to buffer, but source not recognised.");
+            }
+        }
+    }
+
+    @Override
+    public void triggerClientDump(String where, InputMethodManager immInstance,
+            @Nullable byte[] icProto) {
+        // Intentionally left empty, this is implemented in ImeTracingClientImpl
+    }
+
+    @Override
+    public void triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto) {
+        // Intentionally left empty, this is implemented in ImeTracingClientImpl
+    }
+
+    @Override
+    public void triggerManagerServiceDump(String where) {
+        if (!isEnabled() || !isAvailable()) {
+            return;
+        }
+
+        synchronized (mDumpInProgressLock) {
+            if (mDumpInProgress) {
+                return;
+            }
+            mDumpInProgress = true;
+        }
+
+        try {
+            sendToService(null, IME_TRACING_FROM_IMMS, where);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while sending ime-related manager service dump to server", e);
+        } finally {
+            mDumpInProgress = false;
+        }
+    }
+
+    private void writeTracesToFilesLocked() {
+        try {
+            ProtoOutputStream clientsProto = new ProtoOutputStream();
+            clientsProto.write(InputMethodClientsTraceFileProto.MAGIC_NUMBER,
+                    MAGIC_NUMBER_CLIENTS_VALUE);
+            mBufferClients.writeTraceToFile(mTraceFileClients, clientsProto);
+
+            ProtoOutputStream imsProto = new ProtoOutputStream();
+            imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER, MAGIC_NUMBER_IMS_VALUE);
+            mBufferIms.writeTraceToFile(mTraceFileIms, imsProto);
+
+            ProtoOutputStream immsProto = new ProtoOutputStream();
+            immsProto.write(InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER,
+                    MAGIC_NUMBER_IMMS_VALUE);
+            mBufferImms.writeTraceToFile(mTraceFileImms, immsProto);
+
+            resetBuffers();
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        }
+    }
+
+    @GuardedBy("mEnabledLock")
+    @Override
+    public void startTrace(@Nullable PrintWriter pw) {
+        if (IS_USER) {
+            Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+            return;
+        }
+
+        synchronized (mEnabledLock) {
+            if (isAvailable() && isEnabled()) {
+                Log.w(TAG, "Warn: Tracing is already started.");
+                return;
+            }
+
+            logAndPrintln(pw, "Starting tracing in " + TRACE_DIRNAME + ": " + TRACE_FILENAME_CLIENTS
+                    + ", " + TRACE_FILENAME_IMS + ", " + TRACE_FILENAME_IMMS);
+            sEnabled = true;
+            resetBuffers();
+        }
+    }
+
+    @Override
+    public void stopTrace(@Nullable PrintWriter pw) {
+        if (IS_USER) {
+            Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+            return;
+        }
+
+        synchronized (mEnabledLock) {
+            if (!isAvailable() || !isEnabled()) {
+                Log.w(TAG, "Warn: Tracing is not available or not started.");
+                return;
+            }
+
+            logAndPrintln(pw, "Stopping tracing and writing traces in " + TRACE_DIRNAME + ": "
+                    + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", "
+                    + TRACE_FILENAME_IMMS);
+            sEnabled = false;
+            writeTracesToFilesLocked();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void saveForBugreport(@Nullable PrintWriter pw) {
+        if (IS_USER) {
+            return;
+        }
+        synchronized (mEnabledLock) {
+            if (!isAvailable() || !isEnabled()) {
+                return;
+            }
+            // Temporarily stop accepting logs from trace event providers.  There is a small chance
+            // that we may drop some trace events while writing the file, but we currently need to
+            // live with that.  Note that addToBuffer() also has a bug that it doesn't do
+            // read-acquire so flipping sEnabled here doesn't even guarantee that addToBuffer() will
+            // temporarily stop accepting incoming events...
+            // TODO(b/175761228): Implement atomic snapshot to avoid downtime.
+            // TODO(b/175761228): Fix synchronization around sEnabled.
+            sEnabled = false;
+            logAndPrintln(pw, "Writing traces in " + TRACE_DIRNAME + ": "
+                    + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", "
+                    + TRACE_FILENAME_IMMS);
+            writeTracesToFilesLocked();
+            sEnabled = true;
+        }
+    }
+
+    private void resetBuffers() {
+        mBufferClients.resetBuffer();
+        mBufferIms.resetBuffer();
+        mBufferImms.resetBuffer();
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
new file mode 100644
index 0000000..7172d0a
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
@@ -0,0 +1,234 @@
+/*
+ * 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.inputmethod;
+
+import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE;
+import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT;
+import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT;
+import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT;
+import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR;
+import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR;
+import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode;
+import android.view.inputmethod.InputConnectionCallProto.GetExtractedText;
+import android.view.inputmethod.InputConnectionCallProto.GetSelectedText;
+import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText;
+import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor;
+import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor;
+import android.view.inputmethod.SurroundingText;
+
+/**
+ * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are
+ * integrated into {@link ImeTracing}.
+ */
+public final class InputConnectionProtoDumper {
+    static final String TAG = "InputConnectionProtoDumper";
+    public static final boolean DUMP_TEXT = false;
+
+    private InputConnectionProtoDumper() {}
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data.
+     *
+     * @param length The expected length of the text. This must be non-negative.
+     * @param flags Supplies additional options controlling how the text is
+     * returned. May be either {@code 0} or
+     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
+     * @param result The text after the cursor position; the length of the
+     * returned text might be less than <var>length</var>.
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetTextAfterCursorProto(@IntRange(from = 0) int length, int flags,
+            @Nullable CharSequence result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_TEXT_AFTER_CURSOR);
+        proto.write(GetTextAfterCursor.LENGTH, length);
+        proto.write(GetTextAfterCursor.FLAGS, flags);
+        if (result == null) {
+            proto.write(GetTextAfterCursor.RESULT, "null result");
+        } else if (DUMP_TEXT) {
+            proto.write(GetTextAfterCursor.RESULT, result.toString());
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data.
+     *
+     * @param length The expected length of the text. This must be non-negative.
+     * @param flags Supplies additional options controlling how the text is
+     * returned. May be either {@code 0} or
+     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
+     * @param result The text before the cursor position; the length of the
+     * returned text might be less than <var>length</var>.
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
+            int flags, @Nullable CharSequence result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_TEXT_BEFORE_CURSOR);
+        proto.write(GetTextBeforeCursor.LENGTH, length);
+        proto.write(GetTextBeforeCursor.FLAGS, flags);
+        if (result == null) {
+            proto.write(GetTextBeforeCursor.RESULT, "null result");
+        } else if (DUMP_TEXT) {
+            proto.write(GetTextBeforeCursor.RESULT, result.toString());
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data.
+     *
+     * @param flags Supplies additional options controlling how the text is
+     * returned. May be either {@code 0} or
+     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
+     * @param result the text that is currently selected, if any, or null if
+     * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
+     * later, returns false when the target application does not implement
+     * this method.
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetSelectedTextProto(int flags, @Nullable CharSequence result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_SELECTED_TEXT);
+        proto.write(GetSelectedText.FLAGS, flags);
+        if (result == null) {
+            proto.write(GetSelectedText.RESULT, "null result");
+        } else if (DUMP_TEXT) {
+            proto.write(GetSelectedText.RESULT, result.toString());
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data.
+     *
+     * @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 {@code 0} or
+     * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
+     * @param result an {@link 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> .
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetSurroundingTextProto(@IntRange(from = 0) int beforeLength,
+            @IntRange(from = 0) int afterLength, int flags, @Nullable SurroundingText result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_SURROUNDING_TEXT);
+        proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength);
+        proto.write(GetSurroundingText.AFTER_LENGTH, afterLength);
+        proto.write(GetSurroundingText.FLAGS, flags);
+        if (result == null) {
+            final long token_result = proto.start(GetSurroundingText.RESULT);
+            proto.write(GetSurroundingText.SurroundingText.TEXT, "null result");
+            proto.end(token_result);
+        } else if (DUMP_TEXT) {
+            final long token_result = proto.start(GetSurroundingText.RESULT);
+            proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString());
+            proto.write(GetSurroundingText.SurroundingText.SELECTION_START,
+                    result.getSelectionStart());
+            proto.write(GetSurroundingText.SurroundingText.SELECTION_END,
+                    result.getSelectionEnd());
+            proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset());
+            proto.end(token_result);
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data.
+     *
+     * @param reqModes The desired modes to retrieve, as defined by
+     * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.
+     * @param result the caps mode flags that are in effect at the current
+     * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetCursorCapsModeProto(int reqModes, int result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_CURSOR_CAPS_MODE);
+        proto.write(GetCursorCapsMode.REQ_MODES, reqModes);
+        if (DUMP_TEXT) {
+            proto.write(GetCursorCapsMode.RESULT, result);
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+
+    /**
+     * Builder for InputConnectionCallProto to hold
+     * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)}
+     * data.
+     *
+     * @param request Description of how the text should be returned.
+     * {@link ExtractedTextRequest}
+     * @param flags Additional options to control the client, either {@code 0} or
+     * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}.
+     * @param result an {@link ExtractedText}
+     * object describing the state of the text view and containing the
+     * extracted text itself, or null if the input connection is no
+     * longer valid of the editor can't comply with the request for
+     * some reason.
+     * @return Byte-array holding the InputConnectionCallProto data.
+     */
+    @NonNull
+    public static byte[] buildGetExtractedTextProto(@NonNull ExtractedTextRequest
+            request, int flags, @Nullable ExtractedText result) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        final long token = proto.start(GET_EXTRACTED_TEXT);
+        final long token_request = proto.start(REQUEST);
+        proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token);
+        proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags);
+        proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines);
+        proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars);
+        proto.end(token_request);
+        proto.write(GetExtractedText.FLAGS, flags);
+        if (result == null) {
+            proto.write(GetExtractedText.RESULT, "null result");
+        } else if (DUMP_TEXT) {
+            proto.write(GetExtractedText.RESULT, result.text.toString());
+        }
+        proto.end(token);
+        return proto.getBytes();
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
deleted file mode 100644
index 1cf6887..0000000
--- a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
+++ /dev/null
@@ -1,232 +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.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.InputChannel;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInputMethodSession;
-
-/**
- * A utility class to take care of boilerplate code around IPCs.
- *
- * <p>Note: All public methods are designed to be thread-safe.</p>
- */
-public class MultiClientInputMethodPrivilegedOperations {
-    private static final String TAG = "MultiClientInputMethodPrivilegedOperations";
-
-    private static final class OpsHolder {
-        @Nullable
-        @GuardedBy("this")
-        private IMultiClientInputMethodPrivilegedOperations mPrivOps;
-
-        /**
-         * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
-         *
-         * <p>This method can be called only once.</p>
-         *
-         * @param privOps Binder interface to be set.
-         */
-        @AnyThread
-        public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) {
-            if (mPrivOps != null) {
-                throw new IllegalStateException(
-                        "IMultiClientInputMethodPrivilegedOperations must be set at most once."
-                                + " privOps=" + privOps);
-            }
-            mPrivOps = privOps;
-        }
-
-        /**
-         * A simplified version of {@link android.os.Debug#getCaller()}.
-         *
-         * @return method name of the caller.
-         */
-        @AnyThread
-        private static String getCallerMethodName() {
-            final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-            if (callStack.length <= 4) {
-                return "<bottom of call stack>";
-            }
-            return callStack[4].getMethodName();
-        }
-
-        @AnyThread
-        public synchronized void dispose() {
-            mPrivOps = null;
-        }
-
-        @AnyThread
-        @Nullable
-        public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() {
-            if (mPrivOps == null) {
-                Log.e(TAG, getCallerMethodName() + " is ignored."
-                        + " Call it within attachToken() and InputMethodService.onDestroy()");
-            }
-            return mPrivOps;
-        }
-    }
-    private final OpsHolder mOps = new OpsHolder();
-
-    /**
-     * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
-     *
-     * <p>This method can be called only once.</p>
-     *
-     * @param privOps Binder interface to be set.
-     */
-    @AnyThread
-    public void set(IMultiClientInputMethodPrivilegedOperations privOps) {
-        mOps.set(privOps);
-    }
-
-    /**
-     * Disposes internal Binder proxy so that the real Binder object can be garbage collected.
-     */
-    @AnyThread
-    public void dispose() {
-        mOps.dispose();
-    }
-
-    /**
-
-     * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}.
-     *
-     * @param displayId display ID on which the IME window will be shown.
-     * @return Window token to be specified to the IME window.
-     */
-    @AnyThread
-    public IBinder createInputMethodWindowToken(int displayId) {
-        IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return null;
-        }
-        try {
-            return ops.createInputMethodWindowToken(displayId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Calls {@link
-     * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}.
-     *
-     * @param token Window token that is to be deleted.
-     */
-    @AnyThread
-    public void deleteInputMethodWindowToken(IBinder token) {
-        IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return;
-        }
-        try {
-            ops.deleteInputMethodWindowToken(token);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
-     * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
-     *
-     * @param clientId client ID to be accepted.
-     * @param session {@link IInputMethodSession} that is also used for traditional IME protocol.
-     * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks
-     *                           for multi-client scenarios.
-     * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol.
-     */
-    @AnyThread
-    public void acceptClient(int clientId, IInputMethodSession session,
-            IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) {
-        final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return;
-        }
-        try {
-            ops.acceptClient(clientId, session, multiClientSession, writeChannel);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int,
-     * IBinder)}.
-     *
-     * @param clientId client ID about which new IME target window is reported.
-     * @param targetWindowHandle integer handle of the target window.
-     * @param imeWindowToken {@link IBinder} window token of the IME window.
-     */
-    @AnyThread
-    public void reportImeWindowTarget(int clientId, int targetWindowHandle,
-            IBinder imeWindowToken) {
-        final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return;
-        }
-        try {
-            ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}.
-     *
-     * @param displayId display ID to be verified.
-     * @param uid UID to be verified.
-     * @return {@code true} when {@code uid} is allowed to access to {@code displayId}.
-     */
-    @AnyThread
-    public boolean isUidAllowedOnDisplay(int displayId, int uid) {
-        final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return false;
-        }
-        try {
-            return ops.isUidAllowedOnDisplay(displayId, uid);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Calls {@link IMultiClientInputMethodPrivilegedOperations#setActive(int, boolean)}.
-     * @param clientId client ID to be set active/inactive
-     * @param active {@code true} set set active.
-     */
-    @AnyThread
-    public void setActive(int clientId, boolean active) {
-        final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
-        if (ops == null) {
-            return;
-        }
-        try {
-            ops.setActive(clientId, active);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-}
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index c56ed2d..6838cbd 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -20,13 +20,7 @@
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.internal.view.InputBindResult;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -43,15 +37,9 @@
 
     @AnyThread
     @Nullable
-    private static <T> T unwrap(@NonNull AtomicReference<WeakReference<T>> atomicRef) {
-        final WeakReference<T> ref = atomicRef.getAndSet(null);
-        if (ref == null) {
-            // Double-call is guaranteed to be ignored here.
-            return null;
-        }
-        final T value = ref.get();
-        ref.clear();
-        return value;
+    private static <T> T unwrap(@NonNull AtomicReference<T> atomicRef) {
+        // Only the first caller will receive the non-null original object.
+        return atomicRef.getAndSet(null);
     }
 
     /**
@@ -63,8 +51,7 @@
      */
     @AnyThread
     public static IIntResultCallback.Stub of(@NonNull Completable.Int value) {
-        final AtomicReference<WeakReference<Completable.Int>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.Int> atomicRef = new AtomicReference<>(value);
 
         return new IIntResultCallback.Stub() {
             @BinderThread
@@ -100,8 +87,7 @@
     @AnyThread
     public static ICharSequenceResultCallback.Stub of(
             @NonNull Completable.CharSequence value) {
-        final AtomicReference<WeakReference<Completable.CharSequence>> atomicRef =
-                new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.CharSequence> atomicRef = new AtomicReference<>(value);
 
         return new ICharSequenceResultCallback.Stub() {
             @BinderThread
@@ -127,8 +113,7 @@
     @AnyThread
     public static IExtractedTextResultCallback.Stub of(
             @NonNull Completable.ExtractedText value) {
-        final AtomicReference<WeakReference<Completable.ExtractedText>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.ExtractedText> atomicRef = new AtomicReference<>(value);
 
         return new IExtractedTextResultCallback.Stub() {
             @BinderThread
@@ -154,8 +139,7 @@
     @AnyThread
     public static ISurroundingTextResultCallback.Stub of(
             @NonNull Completable.SurroundingText value) {
-        final AtomicReference<WeakReference<Completable.SurroundingText>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.SurroundingText> atomicRef = new AtomicReference<>(value);
 
         return new ISurroundingTextResultCallback.Stub() {
             @BinderThread
@@ -171,43 +155,6 @@
     }
 
     /**
-     * Creates {@link IInputBindResultResultCallback.Stub} that is to set
-     * {@link Completable.InputBindResult} when receiving the result.
-     *
-     * @param value {@link Completable.InputBindResult} to be set when receiving the result.
-     * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC
-     *         parameter.
-     */
-    @AnyThread
-    public static IInputBindResultResultCallback.Stub of(
-            @NonNull Completable.InputBindResult value) {
-        final AtomicReference<WeakReference<Completable.InputBindResult>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
-        return new IInputBindResultResultCallback.Stub() {
-            @BinderThread
-            @Override
-            public void onResult(InputBindResult result) {
-                final Completable.InputBindResult value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onComplete(result);
-            }
-
-            @BinderThread
-            @Override
-            public void onError(ThrowableHolder throwableHolder) {
-                final Completable.InputBindResult value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onError(throwableHolder);
-            }
-        };
-    }
-
-    /**
      * Creates {@link IBooleanResultCallback.Stub} that is to set {@link Completable.Boolean} when
      * receiving the result.
      *
@@ -216,8 +163,7 @@
      */
     @AnyThread
     public static IBooleanResultCallback.Stub of(@NonNull Completable.Boolean value) {
-        final AtomicReference<WeakReference<Completable.Boolean>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.Boolean> atomicRef = new AtomicReference<>(value);
 
         return new IBooleanResultCallback.Stub() {
             @BinderThread
@@ -243,117 +189,6 @@
     }
 
     /**
-     * Creates {@link IInputMethodSubtypeResultCallback.Stub} that is to set
-     * {@link Completable.InputMethodSubtype} when receiving the result.
-     *
-     * @param value {@link Completable.InputMethodSubtype} to be set when receiving the result.
-     * @return {@link IInputMethodSubtypeResultCallback.Stub} that can be passed as a binder
-     * IPC parameter.
-     */
-    @AnyThread
-    public static IInputMethodSubtypeResultCallback.Stub of(
-            @NonNull Completable.InputMethodSubtype value) {
-        final AtomicReference<WeakReference<Completable.InputMethodSubtype>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
-        return new IInputMethodSubtypeResultCallback.Stub() {
-            @BinderThread
-            @Override
-            public void onResult(InputMethodSubtype result) {
-                final Completable.InputMethodSubtype value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onComplete(result);
-            }
-
-            @BinderThread
-            @Override
-            public void onError(ThrowableHolder throwableHolder) {
-                final Completable.InputMethodSubtype value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onError(throwableHolder);
-            }
-        };
-    }
-
-    /**
-     * Creates {@link IInputMethodSubtypeListResultCallback.Stub} that is to set
-     * {@link Completable.InputMethodSubtypeList} when receiving the result.
-     *
-     * @param value {@link Completable.InputMethodSubtypeList} to be set when receiving the result.
-     * @return {@link IInputMethodSubtypeListResultCallback.Stub} that can be passed as a binder
-     * IPC parameter.
-     */
-    @AnyThread
-    public static IInputMethodSubtypeListResultCallback.Stub of(
-            @NonNull Completable.InputMethodSubtypeList value) {
-        final AtomicReference<WeakReference<Completable.InputMethodSubtypeList>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
-        return new IInputMethodSubtypeListResultCallback.Stub() {
-            @BinderThread
-            @Override
-            public void onResult(List<InputMethodSubtype> result) {
-                final Completable.InputMethodSubtypeList value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onComplete(result);
-            }
-
-            @BinderThread
-            @Override
-            public void onError(ThrowableHolder throwableHolder) {
-                final Completable.InputMethodSubtypeList value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onError(throwableHolder);
-            }
-        };
-    }
-
-    /**
-     * Creates {@link IInputMethodInfoListResultCallback.Stub} that is to set
-     * {@link Completable.InputMethodInfoList} when receiving the result.
-     *
-     * @param value {@link Completable.InputMethodInfoList} to be set when receiving the result.
-     * @return {@link IInputMethodInfoListResultCallback.Stub} that can be passed as a binder
-     * IPC parameter.
-     */
-    @AnyThread
-    public static IInputMethodInfoListResultCallback.Stub of(
-            @NonNull Completable.InputMethodInfoList value) {
-        final AtomicReference<WeakReference<Completable.InputMethodInfoList>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
-        return new IInputMethodInfoListResultCallback.Stub() {
-            @BinderThread
-            @Override
-            public void onResult(List<InputMethodInfo> result) {
-                final Completable.InputMethodInfoList value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onComplete(result);
-            }
-
-            @BinderThread
-            @Override
-            public void onError(ThrowableHolder throwableHolder) {
-                final Completable.InputMethodInfoList value = unwrap(atomicRef);
-                if (value == null) {
-                    return;
-                }
-                value.onError(throwableHolder);
-            }
-        };
-    }
-
-    /**
      * Creates {@link IVoidResultCallback.Stub} that is to set {@link Completable.Void} when
      * receiving the result.
      *
@@ -362,8 +197,7 @@
      */
     @AnyThread
     public static IVoidResultCallback.Stub of(@NonNull Completable.Void value) {
-        final AtomicReference<WeakReference<Completable.Void>> atomicRef =
-                new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.Void> atomicRef = new AtomicReference<>(value);
 
         return new IVoidResultCallback.Stub() {
             @BinderThread
@@ -399,8 +233,8 @@
     @AnyThread
     public static IIInputContentUriTokenResultCallback.Stub of(
             @NonNull Completable.IInputContentUriToken value) {
-        final AtomicReference<WeakReference<Completable.IInputContentUriToken>>
-                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+        final AtomicReference<Completable.IInputContentUriToken>
+                atomicRef = new AtomicReference<>(value);
 
         return new IIInputContentUriTokenResultCallback.Stub() {
             @BinderThread
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8c63f38..434f617 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -300,9 +300,9 @@
 
     @VisibleForTesting
     public final class UidToRemove {
-        int startUid;
-        int endUid;
-        long mTimeAddedInQueueMs;
+        private final int mStartUid;
+        private final int mEndUid;
+        private final long mUidRemovalTimestamp;
 
         /** Remove just one UID */
         public UidToRemove(int uid, long timestamp) {
@@ -311,38 +311,18 @@
 
         /** Remove a range of UIDs, startUid must be smaller than endUid. */
         public UidToRemove(int startUid, int endUid, long timestamp) {
-            this.startUid = startUid;
-            this.endUid = endUid;
-            mTimeAddedInQueueMs = timestamp;
+            mStartUid = startUid;
+            mEndUid = endUid;
+            mUidRemovalTimestamp = timestamp;
         }
 
-        void remove() {
-            if (startUid == endUid) {
-                mCpuUidUserSysTimeReader.removeUid(startUid);
-                mCpuUidFreqTimeReader.removeUid(startUid);
-                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
-                    mCpuUidActiveTimeReader.removeUid(startUid);
-                    mCpuUidClusterTimeReader.removeUid(startUid);
-                }
-                if (mKernelSingleUidTimeReader != null) {
-                    mKernelSingleUidTimeReader.removeUid(startUid);
-                }
-                mNumUidsRemoved++;
-            } else if (startUid < endUid) {
-                mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
-                mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
-                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
-                    mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
-                    mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
-                }
-                if (mKernelSingleUidTimeReader != null) {
-                    mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
-                }
-                // Treat as one. We don't know how many uids there are in between.
-                mNumUidsRemoved++;
-            } else {
-                Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
-            }
+        public long getUidRemovalTimestamp() {
+            return mUidRemovalTimestamp;
+        }
+
+        @GuardedBy("BatteryStatsImpl.this")
+        void removeLocked() {
+            removeCpuStatsForUidRangeLocked(mStartUid, mEndUid);
         }
     }
 
@@ -526,11 +506,16 @@
         }
     }
 
-    public void clearPendingRemovedUids() {
+    /**
+     * Removes kernel CPU stats for removed UIDs, in the order they were added to the
+     * mPendingRemovedUids queue.
+     */
+    @GuardedBy("this")
+    public void clearPendingRemovedUidsLocked() {
         long cutOffTimeMs = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
         while (!mPendingRemovedUids.isEmpty()
-                && mPendingRemovedUids.peek().mTimeAddedInQueueMs < cutOffTimeMs) {
-            mPendingRemovedUids.poll().remove();
+                && mPendingRemovedUids.peek().getUidRemovalTimestamp() < cutOffTimeMs) {
+            mPendingRemovedUids.poll().removeLocked();
         }
     }
 
@@ -694,6 +679,8 @@
         Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
         void cancelCpuSyncDueToWakelockChange();
         Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
+        /** Schedule removal of UIDs corresponding to a removed user */
+        Future<?> scheduleCleanupDueToRemovedUser(int userId);
     }
 
     public Handler mHandler;
@@ -5099,46 +5086,48 @@
     }
 
     public void notePowerSaveModeLocked(boolean enabled) {
-        notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), false);
+        notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
     }
 
     /**
-     * Handles power save mode state changes.
+     * Toggles the power save mode state.
      */
-    public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs,
-            boolean forceLog) {
+    public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
+            long uptimeMs) {
+        if (mPowerSaveModeEnabled != enabled) {
+            notePowerSaveModeLocked(enabled, elapsedRealtimeMs, uptimeMs);
+        } else {
+            // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
+            // allow the atom to read all future state changes.
+            FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+                    enabled
+                        ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+                        : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+        }
+    }
+
+    public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
         if (mPowerSaveModeEnabled != enabled) {
             int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
-            mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
-            mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
+            mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+            mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
             mPowerSaveModeEnabled = enabled;
             if (enabled) {
                 mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
-                if (DEBUG_HISTORY) {
-                    Slog.v(TAG, "Power save mode enabled to: "
-                            + Integer.toHexString(mHistoryCur.states2));
-                }
+                if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+                        + Integer.toHexString(mHistoryCur.states2));
                 mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
                 mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
-                if (DEBUG_HISTORY) {
-                    Slog.v(TAG, "Power save mode disabled to: "
-                            + Integer.toHexString(mHistoryCur.states2));
-                }
+                if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+                        + Integer.toHexString(mHistoryCur.states2));
                 mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
             }
             addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
                     enabled
-                            ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
-                            : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
-        } else if (forceLog) {
-            // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
-            // allow the atom to read all future state changes.
-            FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
-                    enabled
-                            ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
-                            : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+                        ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+                        : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -14388,6 +14377,18 @@
     }
 
     public void onUserRemovedLocked(int userId) {
+        if (mExternalSync != null) {
+            // Clear out the removed user's UIDs after a short delay. The delay is needed
+            // because at the point that this method is called, some activities are still
+            // being wrapped up by those UIDs
+            mExternalSync.scheduleCleanupDueToRemovedUser(userId);
+        }
+    }
+
+    /**
+     * Removes battery stats for UIDs corresponding to a removed user.
+     */
+    public void clearRemovedUserUidsLocked(int userId) {
         final int firstUidForUser = UserHandle.getUid(userId, 0);
         final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
         mUidStats.put(firstUidForUser, null);
@@ -14401,6 +14402,7 @@
             }
         }
         mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+        removeCpuStatsForUidRangeLocked(firstUidForUser, lastUidForUser);
     }
 
     /**
@@ -14424,6 +14426,39 @@
     }
 
     /**
+     * Removes the data for the deleted UIDs from the underlying kernel eBPF tables.
+     */
+    @GuardedBy("this")
+    private void removeCpuStatsForUidRangeLocked(int startUid, int endUid) {
+        if (startUid == endUid) {
+            mCpuUidUserSysTimeReader.removeUid(startUid);
+            mCpuUidFreqTimeReader.removeUid(startUid);
+            if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                mCpuUidActiveTimeReader.removeUid(startUid);
+                mCpuUidClusterTimeReader.removeUid(startUid);
+            }
+            if (mKernelSingleUidTimeReader != null) {
+                mKernelSingleUidTimeReader.removeUid(startUid);
+            }
+            mNumUidsRemoved++;
+        } else if (startUid < endUid) {
+            mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
+            mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
+            if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
+                mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
+            }
+            if (mKernelSingleUidTimeReader != null) {
+                mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
+            }
+            // Treat as one. We don't know how many uids there are in between.
+            mNumUidsRemoved++;
+        } else {
+            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+        }
+    }
+
+    /**
      * Retrieve the statistics object for a particular process, creating
      * if needed.
      */
@@ -14727,7 +14762,7 @@
 
         private void updateUidRemoveDelay(long newTimeMs) {
             UID_REMOVE_DELAY_MS = newTimeMs;
-            clearPendingRemovedUids();
+            clearPendingRemovedUidsLocked();
         }
 
         public void dumpLocked(PrintWriter pw) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e4e28a9..92d5a47 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -747,7 +747,7 @@
                             new DataOutputStream(sessionSocket.getOutputStream());
                     Credentials peerCredentials = sessionSocket.getPeerCredentials();
                     tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
-                    args = ZygoteArguments.getInstance(argBuffer);
+                    args = ZygoteArguments.getInstance(tmpArgBuffer);
                     applyUidSecurityPolicy(args, peerCredentials);
                     // TODO (chriswailes): Should this only be run for debug builds?
                     validateUsapCommand(args);
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 60a8d80..d3224b1 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -16,16 +16,20 @@
 
 package com.android.internal.policy;
 
+import static android.view.WindowManager.TRANSIT_CLOSE;
 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;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
 
+import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -34,12 +38,18 @@
 import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.hardware.HardwareBuffer;
 import android.os.SystemProperties;
 import android.util.Slog;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
@@ -56,11 +66,17 @@
 
 /** @hide */
 public class TransitionAnimation {
+    public static final int WALLPAPER_TRANSITION_NONE = 0;
+    public static final int WALLPAPER_TRANSITION_OPEN = 1;
+    public static final int WALLPAPER_TRANSITION_CLOSE = 2;
+    public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
+    public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
+
     // These are the possible states for the enter/exit activities during a thumbnail transition
-    public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
-    public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
-    public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
-    public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
 
     /**
      * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
@@ -72,9 +88,15 @@
 
     public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
 
+    /** Fraction of animation at which the recents thumbnail stays completely transparent */
+    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
     /** Fraction of animation at which the recents thumbnail becomes completely transparent */
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
 
+    /** Interpolator to be used for animations that respond directly to a touch */
+    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
     private static final String DEFAULT_PACKAGE = "android";
 
     private final Context mContext;
@@ -86,7 +108,9 @@
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
     private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
     private final Interpolator mDecelerateInterpolator;
+    private final Interpolator mFastOutLinearInInterpolator;
     private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mThumbnailFadeInInterpolator;
     private final Interpolator mThumbnailFadeOutInterpolator;
     private final Rect mTmpFromClipRect = new Rect();
     private final Rect mTmpToClipRect = new Rect();
@@ -107,8 +131,19 @@
 
         mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.decelerate_cubic);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.linear_out_slow_in);
+        mThumbnailFadeInInterpolator = input -> {
+            // Linear response for first fraction, then complete after that.
+            if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
+                return 0f;
+            }
+            float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION)
+                    / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
+            return mFastOutLinearInInterpolator.getInterpolation(t);
+        };
         mThumbnailFadeOutInterpolator = input -> {
             // Linear response for first fraction, then complete after that.
             if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
@@ -181,6 +216,13 @@
                 DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
     }
 
+    @Nullable
+    public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+        final Animation animation = loadCrossProfileAppThumbnailEnterAnimation();
+        return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+                appRect.height(), 0, null);
+    }
+
     /** Load animation by resource Id from specific package. */
     @Nullable
     public Animation loadAnimationRes(String packageName, int resId) {
@@ -347,8 +389,15 @@
         }
     }
 
-    public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
-            Rect displayFrame, Rect startRect) {
+    public Animation createClipRevealAnimationLocked(@TransitionType int transit,
+            int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
+        return createClipRevealAnimationLockedCompat(
+                getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame,
+                startRect);
+    }
+
+    public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit,
+            boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
         final Animation anim;
         if (enter) {
             final int appWidth = appFrame.width();
@@ -458,8 +507,14 @@
         return anim;
     }
 
-    public Animation createScaleUpAnimationLocked(int transit, boolean enter,
-            Rect containingFrame, Rect startRect) {
+    public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit,
+            boolean enter, Rect containingFrame, Rect startRect) {
+        return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit),
+                enter, containingFrame, startRect);
+    }
+
+    public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit,
+            boolean enter, Rect containingFrame, Rect startRect) {
         Animation a;
         setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
         final int appWidth = containingFrame.width();
@@ -514,12 +569,19 @@
         return a;
     }
 
+    public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp,
+            Rect containingFrame, @TransitionType int transit, int wallpaperTransit,
+            HardwareBuffer thumbnailHeader, Rect startRect) {
+        return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame,
+                getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect);
+    }
+
     /**
      * This animation is created when we are doing a thumbnail transition, for the activity that is
      * leaving, and the activity that is entering.
      */
-    public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
-            Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
+    public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp,
+            Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader,
             Rect startRect) {
         final int appWidth = containingFrame.width();
         final int appHeight = containingFrame.height();
@@ -529,6 +591,7 @@
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
         final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
         final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+        final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
 
         switch (thumbTransitState) {
             case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
@@ -587,8 +650,8 @@
      * This alternate animation is created when we are doing a thumbnail transition, for the
      * activity that is leaving, and the activity that is entering.
      */
-    public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
-            int orientation, int transit, Rect containingFrame, Rect contentInsets,
+    public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter,
+            boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets,
             @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
             Rect startRect, Rect defaultStartRect) {
         Animation a;
@@ -601,11 +664,11 @@
         final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
         final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
         final int thumbStartY = mTmpRect.top - containingFrame.top;
+        final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
 
         switch (thumbTransitState) {
             case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
             case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
-                final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
                 if (freeform && scaleUp) {
                     a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
                             containingFrame, surfaceInsets, startRect, defaultStartRect);
@@ -720,10 +783,151 @@
     }
 
     /**
+     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+     * when a thumbnail is specified with the pending animation override.
+     */
+    public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect,
+            @Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation,
+            Rect startRect, Rect defaultStartRect, boolean scaleUp) {
+        Animation a;
+        final int thumbWidthI = thumbnailHeader.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = thumbnailHeader.getHeight();
+        final int appWidth = appRect.width();
+
+        float scaleW = appWidth / thumbWidth;
+        getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+        final float fromX;
+        float fromY;
+        final float toX;
+        float toY;
+        final float pivotX;
+        final float pivotY;
+        if (shouldScaleDownThumbnailTransition(orientation)) {
+            fromX = mTmpRect.left;
+            fromY = mTmpRect.top;
+
+            // For the curved translate animation to work, the pivot points needs to be at the
+            // same absolute position as the one from the real surface.
+            toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+            toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+            pivotX = mTmpRect.width() / 2;
+            pivotY = appRect.height() / 2 / scaleW;
+            if (mGridLayoutRecentsEnabled) {
+                // In the grid layout, the header is displayed above the thumbnail instead of
+                // overlapping it.
+                fromY -= thumbHeightI;
+                toY -= thumbHeightI * scaleW;
+            }
+        } else {
+            pivotX = 0;
+            pivotY = 0;
+            fromX = mTmpRect.left;
+            fromY = mTmpRect.top;
+            toX = appRect.left;
+            toY = appRect.top;
+        }
+        if (scaleUp) {
+            // Animation up from the thumbnail to the full screen
+            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+            Animation alpha = new AlphaAnimation(1f, 0f);
+            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+            alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+            Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+            mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+            mTmpToClipRect.set(appRect);
+
+            // Containing frame is in screen space, but we need the clip rect in the
+            // app space.
+            mTmpToClipRect.offsetTo(0, 0);
+            mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+            if (contentInsets != null) {
+                mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+                        (int) (-contentInsets.top * scaleW),
+                        (int) (-contentInsets.right * scaleW),
+                        (int) (-contentInsets.bottom * scaleW));
+            }
+
+            Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+            clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+            clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            if (!mGridLayoutRecentsEnabled) {
+                // In the grid layout, the header should be shown for the whole animation.
+                set.addAnimation(alpha);
+            }
+            set.addAnimation(translate);
+            set.addAnimation(clipAnim);
+            a = set;
+        } else {
+            // Animation down from the full screen to the thumbnail
+            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+            Animation alpha = new AlphaAnimation(0f, 1f);
+            alpha.setInterpolator(mThumbnailFadeInInterpolator);
+            alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+            Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            if (!mGridLayoutRecentsEnabled) {
+                // In the grid layout, the header should be shown for the whole animation.
+                set.addAnimation(alpha);
+            }
+            set.addAnimation(translate);
+            a = set;
+
+        }
+        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
+                null);
+    }
+
+    /**
+     * Creates an overlay with a background color and a thumbnail for the cross profile apps
+     * animation.
+     */
+    public HardwareBuffer createCrossProfileAppsThumbnail(
+            @DrawableRes int thumbnailDrawableRes, Rect frame) {
+        final int width = frame.width();
+        final int height = frame.height();
+
+        final Picture picture = new Picture();
+        final Canvas canvas = picture.beginRecording(width, height);
+        canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+        final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+        final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
+        drawable.setBounds(
+                (width - thumbnailSize) / 2,
+                (height - thumbnailSize) / 2,
+                (width + thumbnailSize) / 2,
+                (height + thumbnailSize) / 2);
+        drawable.setTint(mContext.getColor(android.R.color.white));
+        drawable.draw(canvas);
+        picture.endRecording();
+
+        return Bitmap.createBitmap(picture).getHardwareBuffer();
+    }
+
+    /**
      * Prepares the specified animation with a standard duration, interpolator, etc.
      */
     private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
-            int transit) {
+            @TransitionOldType int transit) {
         // Pick the desired duration.  If this is an inter-activity transition,
         // it  is the standard duration for that.  Otherwise we use the longer
         // task transition duration.
@@ -820,6 +1024,22 @@
         return anim;
     }
 
+    private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
+            int wallpaperTransit) {
+        if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+            return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+            return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+        } else if (transit == TRANSIT_OPEN) {
+            return TRANSIT_OLD_ACTIVITY_OPEN;
+        } else if (transit == TRANSIT_CLOSE) {
+            return TRANSIT_OLD_ACTIVITY_CLOSE;
+        }
+
+        // We only do some special handle for above type, so use type NONE for default behavior.
+        return TRANSIT_OLD_NONE;
+    }
+
     /**
      * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
      * the start rect is outside of the target rect, and there is a lot of movement going on.
@@ -843,10 +1063,33 @@
     }
 
     /**
+     * Return the current thumbnail transition state.
+     */
+    private int getThumbnailTransitionState(boolean enter, boolean scaleUp) {
+        if (enter) {
+            if (scaleUp) {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+            }
+        } else {
+            if (scaleUp) {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+            }
+        }
+    }
+
+    /**
      * Prepares the specified animation with a standard duration, interpolator, etc.
      */
-    private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
+    public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
             int appHeight, long duration, Interpolator interpolator) {
+        if (a == null) {
+            return null;
+        }
+
         if (duration > 0) {
             a.setDuration(duration);
         }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 10f14b4..4f940db 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.service.notification.StatusBarNotification;
+import android.view.InsetsState;
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.view.AppearanceRegion;
@@ -182,7 +183,7 @@
     /**
      * Notifies System UI side of system bar attribute change on the specified display.
      *
-     * @param displayId the ID of the display to notify
+     * @param displayId the ID of the display to notify.
      * @param appearance the appearance of the focused window. The light top bar appearance is not
      *                   controlled here, but primaryAppearance and secondaryAppearance.
      * @param appearanceRegions a set of appearances which will be only applied in their own bounds.
@@ -191,11 +192,12 @@
      *                         stacks.
      * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
      * @param behavior the behavior of the focused window.
-     * @param isFullscreen whether any of status or navigation bar is requested invisible.
+     * @param requestedState the collection of the requested visibilities of system insets.
+     * @param packageName the package name of the focused app.
      */
     void onSystemBarAttributesChanged(int displayId, int appearance,
             in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            int behavior, boolean isFullscreen);
+            int behavior, in InsetsState requestedVisibilities, String packageName);
 
     /**
      * Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8fb2f9c..2fd1691 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
+import android.view.InsetsState;
 
 import com.android.internal.view.AppearanceRegion;
 
@@ -39,14 +40,15 @@
     public final IBinder mImeToken;
     public final boolean mNavbarColorManagedByIme;
     public final int mBehavior;
-    public final boolean mAppFullscreen;
+    public final InsetsState mRequestedState;
+    public final String mPackageName;
     public final int[] mTransientBarTypes;
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
             int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
             int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
-            boolean navbarColorManagedByIme, int behavior, boolean appFullscreen,
-            @NonNull int[] transientBarTypes) {
+            boolean navbarColorManagedByIme, int behavior, InsetsState requestedState,
+            String packageName, @NonNull int[] transientBarTypes) {
         mIcons = new ArrayMap<>(icons);
         mDisabledFlags1 = disabledFlags1;
         mAppearance = appearance;
@@ -58,7 +60,8 @@
         mImeToken = imeToken;
         mNavbarColorManagedByIme = navbarColorManagedByIme;
         mBehavior = behavior;
-        mAppFullscreen = appFullscreen;
+        mRequestedState = requestedState;
+        mPackageName = packageName;
         mTransientBarTypes = transientBarTypes;
     }
 
@@ -80,7 +83,8 @@
         dest.writeStrongBinder(mImeToken);
         dest.writeBoolean(mNavbarColorManagedByIme);
         dest.writeInt(mBehavior);
-        dest.writeBoolean(mAppFullscreen);
+        dest.writeTypedObject(mRequestedState, 0);
+        dest.writeString(mPackageName);
         dest.writeIntArray(mTransientBarTypes);
     }
 
@@ -104,12 +108,13 @@
                     final IBinder imeToken = source.readStrongBinder();
                     final boolean navbarColorManagedByIme = source.readBoolean();
                     final int behavior = source.readInt();
-                    final boolean appFullscreen = source.readBoolean();
+                    final InsetsState requestedState = source.readTypedObject(InsetsState.CREATOR);
+                    final String packageName = source.readString();
                     final int[] transientBarTypes = source.createIntArray();
                     return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
                             appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
                             disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
-                            appFullscreen, transientBarTypes);
+                            requestedState, packageName, transientBarTypes);
                 }
 
                 @Override
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index a045451..5b68159 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,5 +1,6 @@
 per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
 per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file Protocol* = etancohen@google.com, lorenzo@google.com
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 783d088..4607129 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -18,8 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -27,8 +25,6 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
 import android.util.proto.ProtoOutputStream;
 import android.view.KeyEvent;
 import android.view.View;
@@ -45,10 +41,13 @@
 import android.view.inputmethod.SurroundingText;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IBooleanResultCallback;
 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.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputConnectionProtoDumper;
 import com.android.internal.os.SomeArgs;
 
 import java.lang.ref.WeakReference;
@@ -79,7 +78,7 @@
     private static final int DO_PERFORM_SPELL_CHECK = 110;
     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     private static final int DO_CLEAR_META_KEY_STATES = 130;
-    private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
+    private static final int DO_REQUEST_CURSOR_UPDATES = 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;
@@ -88,12 +87,11 @@
 
     @GuardedBy("mLock")
     @Nullable
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private InputConnection mInputConnection;
 
     private Looper mMainLooper;
     private Handler mH;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+
     private final Object mLock = new Object();
     @GuardedBy("mLock")
     private boolean mFinished = false;
@@ -309,8 +307,8 @@
         dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
     }
 
-    public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback) {
-        dispatchMessage(mH.obtainMessage(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
+    public void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback) {
+        dispatchMessage(mH.obtainMessage(DO_REQUEST_CURSOR_UPDATES, cursorUpdateMode,
                 0 /* unused */, callback));
     }
 
@@ -319,7 +317,7 @@
     }
 
     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
-            IIntResultCallback callback) {
+            IBooleanResultCallback callback) {
         final SomeArgs args = SomeArgs.obtain();
         args.arg1 = inputContentInfo;
         args.arg2 = opts;
@@ -350,7 +348,7 @@
     }
 
     void executeMessage(Message msg) {
-        ProtoOutputStream icProto;
+        byte[] icProto;
         switch (msg.what) {
             case DO_GET_TEXT_AFTER_CURSOR: {
                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
@@ -366,7 +364,7 @@
                         result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
+                        icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
@@ -396,7 +394,7 @@
                         result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
+                        icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
@@ -426,7 +424,8 @@
                         result = ic.getSelectedText(msg.arg1);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
+                        icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(msg.arg1,
+                                result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getSelectedText", mParentInputMethodManager, icProto);
                     }
@@ -459,8 +458,8 @@
                         result = ic.getSurroundingText(beforeLength, afterLength, flags);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
-                                afterLength, flags, result);
+                        icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
+                                beforeLength, afterLength, flags, result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
                     }
@@ -489,7 +488,7 @@
                         result = ic.getCursorCapsMode(msg.arg1);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
+                        icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(msg.arg1,
                                 result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
@@ -521,7 +520,7 @@
                         result = ic.getExtractedText(request, msg.arg1);
                     }
                     if (ImeTracing.getInstance().isEnabled()) {
-                        icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
+                        icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(request,
                                 msg.arg1, result);
                         ImeTracing.getInstance().triggerClientDump(
                                 TAG + "#getExtractedText", mParentInputMethodManager, icProto);
@@ -793,10 +792,10 @@
                 }
                 return;
             }
-            case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
+            case DO_REQUEST_CURSOR_UPDATES: {
                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
                 try {
-                    final IIntResultCallback callback = (IIntResultCallback) msg.obj;
+                    final IBooleanResultCallback callback = (IBooleanResultCallback) msg.obj;
                     final InputConnection ic = getInputConnection();
                     final boolean result;
                     if (ic == null || !isActive()) {
@@ -806,7 +805,7 @@
                         result = ic.requestCursorUpdates(msg.arg1);
                     }
                     try {
-                        callback.onResult(result ? 1 : 0);
+                        callback.onResult(result);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
                                 + " result=" + result, e);
@@ -853,7 +852,7 @@
                 SomeArgs args = (SomeArgs) msg.obj;
                 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
                 try {
-                    final IIntResultCallback callback = (IIntResultCallback) args.arg3;
+                    final IBooleanResultCallback callback = (IBooleanResultCallback) args.arg3;
                     final InputConnection ic = getInputConnection();
                     final boolean result;
                     if (ic == null || !isActive()) {
@@ -870,7 +869,7 @@
                         }
                     }
                     try {
-                        callback.onResult(result ? 1 : 0);
+                        callback.onResult(result);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to return the result to commitContent()."
                                 + " result=" + result, e);
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index b06b4e5..dd42c40e 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -23,6 +23,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputContentInfo;
 
+import com.android.internal.inputmethod.IBooleanResultCallback;
 import com.android.internal.inputmethod.ICharSequenceResultCallback;
 import com.android.internal.inputmethod.IExtractedTextResultCallback;
 import com.android.internal.inputmethod.IIntResultCallback;
@@ -78,10 +79,10 @@
 
     void getSelectedText(int flags, ICharSequenceResultCallback callback);
 
-    void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback);
+    void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback);
 
     void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
-            IIntResultCallback callback);
+            IBooleanResultCallback callback);
 
     void getSurroundingText(int beforeLength, int afterLength, int flags,
             ISurroundingTextResultCallback callback);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33..5354afb 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,7 +35,7 @@
  * {@hide}
  */
 oneway interface IInputMethod {
-    void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
+    void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
              int configChanges);
 
     void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index df371ce..92105b9 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -53,8 +53,7 @@
             ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
             ResultCode.ERROR_NO_EDITOR,
             ResultCode.ERROR_DISPLAY_ID_MISMATCH,
-            ResultCode.ERROR_INVALID_DISPLAY_ID,
-            ResultCode.ERROR_INVALID_CLIENT,
+            ResultCode.ERROR_INVALID_DISPLAY_ID
     })
     public @interface ResultCode {
         /**
@@ -168,10 +167,6 @@
          * display.
          */
         int ERROR_INVALID_DISPLAY_ID = 15;
-        /**
-         * Indicates that the client is not recognized by the system.
-         */
-        int ERROR_INVALID_CLIENT = 16;
     }
 
     @ResultCode
@@ -310,8 +305,6 @@
                 return "ERROR_DISPLAY_ID_MISMATCH";
             case ResultCode.ERROR_INVALID_DISPLAY_ID:
                 return "ERROR_INVALID_DISPLAY_ID";
-            case ResultCode.ERROR_INVALID_CLIENT:
-                return "ERROR_INVALID_CLIENT";
             default:
                 return "Unknown(" + result + ")";
         }
@@ -370,11 +363,6 @@
             error(ResultCode.ERROR_INVALID_DISPLAY_ID);
 
     /**
-     * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
-     */
-    public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
-
-    /**
      * Predefined <strong>success</strong> object for
      * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
      */
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
deleted file mode 100644
index 0e9d135..0000000
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * 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.internal.view;
-
-import android.annotation.AnyThread;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
-import android.util.proto.ProtoOutputStream;
-import android.view.KeyEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-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.Completable;
-import com.android.internal.inputmethod.ResultCallbacks;
-
-import java.lang.ref.WeakReference;
-
-public class InputConnectionWrapper implements InputConnection {
-    private static final String TAG = "InputConnectionWrapper";
-
-    private static final int MAX_WAIT_TIME_MILLIS = 2000;
-    private final IInputContext mIInputContext;
-    @NonNull
-    private final WeakReference<AbstractInputMethodService> mInputMethodService;
-
-    @MissingMethodFlags
-    private final int mMissingMethods;
-
-    /**
-     * Signaled when the system decided to take away IME focus from the target app.
-     *
-     * <p>This is expected to be signaled immediately when the IME process receives
-     * {@link IInputMethod#unbindInput()}.</p>
-     */
-    @NonNull
-    private final CancellationGroup mCancellationGroup;
-
-    public InputConnectionWrapper(
-            @NonNull WeakReference<AbstractInputMethodService> inputMethodService,
-            IInputContext inputContext, @MissingMethodFlags int missingMethods,
-            @NonNull CancellationGroup cancellationGroup) {
-        mInputMethodService = inputMethodService;
-        mIInputContext = inputContext;
-        mMissingMethods = missingMethods;
-        mCancellationGroup = cancellationGroup;
-    }
-
-    /**
-     * See {@link InputConnection#getTextAfterCursor(int, int)}.
-     */
-    @Nullable
-    @AnyThread
-    public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0 || mCancellationGroup.isCanceled()) {
-            return null;
-        }
-
-        final Completable.CharSequence value = Completable.createCharSequence();
-        try {
-            mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return null;
-        }
-        CharSequence result = Completable.getResultOrNull(
-                value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length,
-                    flags, result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    /**
-     * See {@link InputConnection#getTextBeforeCursor(int, int)}.
-     */
-    @Nullable
-    @AnyThread
-    public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0 || mCancellationGroup.isCanceled()) {
-            return null;
-        }
-
-        final Completable.CharSequence value = Completable.createCharSequence();
-        try {
-            mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return null;
-        }
-        CharSequence result = Completable.getResultOrNull(
-                value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length,
-                    flags, result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    @AnyThread
-    public CharSequence getSelectedText(int flags) {
-        if (mCancellationGroup.isCanceled()) {
-            return null;
-        }
-
-        if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
-            // This method is not implemented.
-            return null;
-        }
-        final Completable.CharSequence value = Completable.createCharSequence();
-        try {
-            mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return null;
-        }
-        CharSequence result = Completable.getResultOrNull(
-                value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags,
-                    result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    /**
-     * 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(
-            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
-        if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) {
-            return null;
-        }
-
-        if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
-            // This method is not implemented.
-            return null;
-        }
-        final Completable.SurroundingText value = Completable.createSurroundingText();
-        try {
-            mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
-                    ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return null;
-        }
-        SurroundingText result = Completable.getResultOrNull(
-                value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto(
-                    beforeLength, afterLength, flags, result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    @AnyThread
-    public int getCursorCapsMode(int reqModes) {
-        if (mCancellationGroup.isCanceled()) {
-            return 0;
-        }
-
-        final Completable.Int value = Completable.createInt();
-        try {
-            mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return 0;
-        }
-        int result = Completable.getResultOrZero(
-                value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto(
-                    reqModes, result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    @AnyThread
-    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
-        if (mCancellationGroup.isCanceled()) {
-            return null;
-        }
-
-        final Completable.ExtractedText value = Completable.createExtractedText();
-        try {
-            mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return null;
-        }
-        ExtractedText result = Completable.getResultOrNull(
-                value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
-
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
-            ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto(
-                    request, flags, result);
-            ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText",
-                    inputMethodService, icProto);
-        }
-
-        return result;
-    }
-
-    @AnyThread
-    public boolean commitText(CharSequence text, int newCursorPosition) {
-        try {
-            mIInputContext.commitText(text, newCursorPosition);
-            notifyUserActionIfNecessary();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    private void notifyUserActionIfNecessary() {
-        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-        if (inputMethodService == null) {
-            // This basically should not happen, because it's the the caller of this method.
-            return;
-        }
-        inputMethodService.notifyUserActionIfNecessary();
-    }
-
-    @AnyThread
-    public boolean commitCompletion(CompletionInfo text) {
-        if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
-            // This method is not implemented.
-            return false;
-        }
-        try {
-            mIInputContext.commitCompletion(text);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean commitCorrection(CorrectionInfo correctionInfo) {
-        try {
-            mIInputContext.commitCorrection(correctionInfo);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean setSelection(int start, int end) {
-        try {
-            mIInputContext.setSelection(start, end);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean performEditorAction(int actionCode) {
-        try {
-            mIInputContext.performEditorAction(actionCode);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean performContextMenuAction(int id) {
-        try {
-            mIInputContext.performContextMenuAction(id);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean setComposingRegion(int start, int end) {
-        if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
-            // This method is not implemented.
-            return false;
-        }
-        try {
-            mIInputContext.setComposingRegion(start, end);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean setComposingText(CharSequence text, int newCursorPosition) {
-        try {
-            mIInputContext.setComposingText(text, newCursorPosition);
-            notifyUserActionIfNecessary();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean finishComposingText() {
-        try {
-            mIInputContext.finishComposingText();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean beginBatchEdit() {
-        try {
-            mIInputContext.beginBatchEdit();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean endBatchEdit() {
-        try {
-            mIInputContext.endBatchEdit();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean sendKeyEvent(KeyEvent event) {
-        try {
-            mIInputContext.sendKeyEvent(event);
-            notifyUserActionIfNecessary();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean clearMetaKeyStates(int states) {
-        try {
-            mIInputContext.clearMetaKeyStates(states);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
-        try {
-            mIInputContext.deleteSurroundingText(beforeLength, afterLength);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
-        if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
-            // This method is not implemented.
-            return false;
-        }
-        try {
-            mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean reportFullscreenMode(boolean enabled) {
-        // Nothing should happen when called from input method.
-        return false;
-    }
-
-    @AnyThread
-    @Override
-    public boolean performSpellCheck() {
-        try {
-            mIInputContext.performSpellCheck();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean performPrivateCommand(String action, Bundle data) {
-        try {
-            mIInputContext.performPrivateCommand(action, data);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    public boolean requestCursorUpdates(int cursorUpdateMode) {
-        if (mCancellationGroup.isCanceled()) {
-            return false;
-        }
-
-        if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
-            // This method is not implemented.
-            return false;
-        }
-        final Completable.Int value = Completable.createInt();
-        try {
-            mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode,
-                    ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return false;
-        }
-        return Completable.getResultOrZero(value, TAG, "requestUpdateCursorAnchorInfo()",
-                mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
-    }
-
-    @AnyThread
-    public Handler getHandler() {
-        // Nothing should happen when called from input method.
-        return null;
-    }
-
-    @AnyThread
-    public void closeConnection() {
-        // Nothing should happen when called from input method.
-    }
-
-    @AnyThread
-    public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
-        if (mCancellationGroup.isCanceled()) {
-            return false;
-        }
-
-        if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
-            // This method is not implemented.
-            return false;
-        }
-
-        if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
-            final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-            if (inputMethodService == null) {
-                // This basically should not happen, because it's the caller of this method.
-                return false;
-            }
-            inputMethodService.exposeContent(inputContentInfo, this);
-        }
-
-        final Completable.Int value = Completable.createInt();
-        try {
-            mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
-        } catch (RemoteException e) {
-            return false;
-        }
-        return Completable.getResultOrZero(
-                value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
-    }
-
-    /**
-     * See {@link InputConnection#setImeConsumesInput(boolean)}.
-     */
-    @AnyThread
-    public boolean setImeConsumesInput(boolean imeConsumesInput) {
-        try {
-            mIInputContext.setImeConsumesInput(imeConsumesInput);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @AnyThread
-    private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
-        return (mMissingMethods & methodFlag) == methodFlag;
-    }
-
-    @AnyThread
-    @Override
-    public String toString() {
-        return "InputConnectionWrapper{idHash=#"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " mMissingMethods="
-                + InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
-    }
-}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
deleted file mode 100644
index 02ffe8c..0000000
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2007-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.internal.widget;
-
-import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE;
-import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT;
-import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT;
-import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
-import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.Selection;
-import android.text.method.KeyListener;
-import android.util.Log;
-import android.util.imetracing.InputConnectionHelper;
-import android.util.proto.ProtoOutputStream;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.DumpableInputConnection;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-import android.widget.TextView;
-
-/**
- * Base class for an editable InputConnection instance. This is created by {@link TextView} or
- * {@link EditText}.
- */
-public class EditableInputConnection extends BaseInputConnection
-        implements DumpableInputConnection {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "EditableInputConnection";
-
-    private final TextView mTextView;
-
-    // Keeps track of nested begin/end batch edit to ensure this connection always has a
-    // balanced impact on its associated TextView.
-    // A negative value means that this connection has been finished by the InputMethodManager.
-    private int mBatchEditNesting;
-
-    @UnsupportedAppUsage
-    public EditableInputConnection(TextView textview) {
-        super(textview, true);
-        mTextView = textview;
-    }
-
-    @Override
-    public Editable getEditable() {
-        TextView tv = mTextView;
-        if (tv != null) {
-            return tv.getEditableText();
-        }
-        return null;
-    }
-
-    @Override
-    public boolean beginBatchEdit() {
-        synchronized(this) {
-            if (mBatchEditNesting >= 0) {
-                mTextView.beginBatchEdit();
-                mBatchEditNesting++;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean endBatchEdit() {
-        synchronized(this) {
-            if (mBatchEditNesting > 0) {
-                // When the connection is reset by the InputMethodManager and reportFinish
-                // is called, some endBatchEdit calls may still be asynchronously received from the
-                // IME. Do not take these into account, thus ensuring that this IC's final
-                // contribution to mTextView's nested batch edit count is zero.
-                mTextView.endBatchEdit();
-                mBatchEditNesting--;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void endComposingRegionEditInternal() {
-        // The ContentCapture service is interested in Composing-state changes.
-        mTextView.notifyContentCaptureTextChanged();
-    }
-
-    @Override
-    public void closeConnection() {
-        super.closeConnection();
-        synchronized(this) {
-            while (mBatchEditNesting > 0) {
-                endBatchEdit();
-            }
-            // Will prevent any further calls to begin or endBatchEdit
-            mBatchEditNesting = -1;
-        }
-    }
-
-    @Override
-    public boolean clearMetaKeyStates(int states) {
-        final Editable content = getEditable();
-        if (content == null) return false;
-        KeyListener kl = mTextView.getKeyListener();
-        if (kl != null) {
-            try {
-                kl.clearMetaKeyState(mTextView, content, states);
-            } catch (AbstractMethodError e) {
-                // This is an old listener that doesn't implement the
-                // new method.
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public boolean commitCompletion(CompletionInfo text) {
-        if (DEBUG) Log.v(TAG, "commitCompletion " + text);
-        mTextView.beginBatchEdit();
-        mTextView.onCommitCompletion(text);
-        mTextView.endBatchEdit();
-        return true;
-    }
-
-    /**
-     * Calls the {@link TextView#onCommitCorrection} method of the associated TextView.
-     */
-    @Override
-    public boolean commitCorrection(CorrectionInfo correctionInfo) {
-        if (DEBUG) Log.v(TAG, "commitCorrection" + correctionInfo);
-        mTextView.beginBatchEdit();
-        mTextView.onCommitCorrection(correctionInfo);
-        mTextView.endBatchEdit();
-        return true;
-    }
-
-    @Override
-    public boolean performEditorAction(int actionCode) {
-        if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode);
-        mTextView.onEditorAction(actionCode);
-        return true;
-    }
-    
-    @Override
-    public boolean performContextMenuAction(int id) {
-        if (DEBUG) Log.v(TAG, "performContextMenuAction " + id);
-        mTextView.beginBatchEdit();
-        mTextView.onTextContextMenuItem(id);
-        mTextView.endBatchEdit();
-        return true;
-    }
-    
-    @Override
-    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
-        if (mTextView != null) {
-            ExtractedText et = new ExtractedText();
-            if (mTextView.extractText(request, et)) {
-                if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) {
-                    mTextView.setExtracting(request);
-                }
-                return et;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean performSpellCheck() {
-        mTextView.onPerformSpellCheck();
-        return true;
-    }
-
-    @Override
-    public boolean performPrivateCommand(String action, Bundle data) {
-        mTextView.onPrivateIMECommand(action, data);
-        return true;
-    }
-
-    @Override
-    public boolean commitText(CharSequence text, int newCursorPosition) {
-        if (mTextView == null) {
-            return super.commitText(text, newCursorPosition);
-        }
-        mTextView.resetErrorChangedFlag();
-        boolean success = super.commitText(text, newCursorPosition);
-        mTextView.hideErrorIfUnchanged();
-
-        return success;
-    }
-
-    @Override
-    public boolean requestCursorUpdates(int cursorUpdateMode) {
-        if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
-
-        // It is possible that any other bit is used as a valid flag in a future release.
-        // We should reject the entire request in such a case.
-        final int KNOWN_FLAGS_MASK = InputConnection.CURSOR_UPDATE_IMMEDIATE |
-                InputConnection.CURSOR_UPDATE_MONITOR;
-        final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
-        if (unknownFlags != 0) {
-            if (DEBUG) {
-                Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
-                        " cursorUpdateMode=" + cursorUpdateMode +
-                        " unknownFlags=" + unknownFlags);
-            }
-            return false;
-        }
-
-        if (mIMM == null) {
-            // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled.
-            // TODO: Return some notification code rather than false to indicate method that
-            // CursorAnchorInfo is temporarily unavailable.
-            return false;
-        }
-        mIMM.setUpdateCursorAnchorInfoMode(cursorUpdateMode);
-        if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) {
-            if (mTextView == null) {
-                // In this case, FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is silently ignored.
-                // TODO: Return some notification code for the input method that indicates
-                // FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is ignored.
-            } else if (mTextView.isInLayout()) {
-                // In this case, the view hierarchy is currently undergoing a layout pass.
-                // IMM#updateCursorAnchorInfo is supposed to be called soon after the layout
-                // pass is finished.
-            } else {
-                // This will schedule a layout pass of the view tree, and the layout event
-                // eventually triggers IMM#updateCursorAnchorInfo.
-                mTextView.requestLayout();
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public boolean setImeConsumesInput(boolean imeConsumesInput) {
-        if (mTextView == null) {
-            return super.setImeConsumesInput(imeConsumesInput);
-        }
-        mTextView.setImeConsumesInput(imeConsumesInput);
-        return true;
-    }
-
-    @Override
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        CharSequence editableText = mTextView.getText();
-        CharSequence selectedText = getSelectedText(0 /* flags */);
-        if (InputConnectionHelper.DUMP_TEXT) {
-            if (editableText != null) {
-                proto.write(EDITABLE_TEXT, editableText.toString());
-            }
-            if (selectedText != null) {
-                proto.write(SELECTED_TEXT, selectedText.toString());
-            }
-        }
-        final Editable content = getEditable();
-        if (content != null) {
-            int start = Selection.getSelectionStart(content);
-            int end = Selection.getSelectionEnd(content);
-            proto.write(SELECTED_TEXT_START, start);
-            proto.write(SELECTED_TEXT_END, end);
-        }
-        proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0));
-        proto.end(token);
-    }
-}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index fd6038f..4fc135c 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -19,6 +19,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.Path;
@@ -136,6 +137,7 @@
     private final FontMetricsInt mTextMetrics = new FontMetricsInt();
     private int mHeaderBottom;
     private int mHeaderPaddingTop = 0;
+    private Insets mWaterfallInsets = Insets.NONE;
     @UnsupportedAppUsage
     private boolean mCurDown;
     @UnsupportedAppUsage
@@ -229,8 +231,10 @@
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         if (insets.getDisplayCutout() != null) {
             mHeaderPaddingTop = insets.getDisplayCutout().getSafeInsetTop();
+            mWaterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
         } else {
             mHeaderPaddingTop = 0;
+            mWaterfallInsets = Insets.NONE;
         }
         return super.onApplyWindowInsets(insets);
     }
@@ -266,11 +270,6 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        final int w = getWidth();
-        final int itemW = w/7;
-        final int base = mHeaderPaddingTop-mTextMetrics.ascent+1;
-        final int bottom = mHeaderBottom;
-
         final int NP = mPointers.size();
 
         if (!mSystemGestureExclusion.isEmpty()) {
@@ -286,71 +285,7 @@
         }
 
         // Labels
-        if (mActivePointerId >= 0) {
-            final PointerState ps = mPointers.get(mActivePointerId);
-            
-            canvas.drawRect(0, mHeaderPaddingTop, itemW-1, bottom,mTextBackgroundPaint);
-            canvas.drawText(mText.clear()
-                    .append("P: ").append(mCurNumPointers)
-                    .append(" / ").append(mMaxNumPointers)
-                    .toString(), 1, base, mTextPaint);
-
-            final int N = ps.mTraceCount;
-            if ((mCurDown && ps.mCurDown) || N == 0) {
-                canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
-                        mTextBackgroundPaint);
-                canvas.drawText(mText.clear()
-                        .append("X: ").append(ps.mCoords.x, 1)
-                        .toString(), 1 + itemW, base, mTextPaint);
-                canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
-                        mTextBackgroundPaint);
-                canvas.drawText(mText.clear()
-                        .append("Y: ").append(ps.mCoords.y, 1)
-                        .toString(), 1 + itemW * 2, base, mTextPaint);
-            } else {
-                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
-                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
-                canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
-                        Math.abs(dx) < mVC.getScaledTouchSlop()
-                        ? mTextBackgroundPaint : mTextLevelPaint);
-                canvas.drawText(mText.clear()
-                        .append("dX: ").append(dx, 1)
-                        .toString(), 1 + itemW, base, mTextPaint);
-                canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
-                        Math.abs(dy) < mVC.getScaledTouchSlop()
-                        ? mTextBackgroundPaint : mTextLevelPaint);
-                canvas.drawText(mText.clear()
-                        .append("dY: ").append(dy, 1)
-                        .toString(), 1 + itemW * 2, base, mTextPaint);
-            }
-
-            canvas.drawRect(itemW * 3, mHeaderPaddingTop, (itemW * 4) - 1, bottom,
-                    mTextBackgroundPaint);
-            canvas.drawText(mText.clear()
-                    .append("Xv: ").append(ps.mXVelocity, 3)
-                    .toString(), 1 + itemW * 3, base, mTextPaint);
-
-            canvas.drawRect(itemW * 4, mHeaderPaddingTop, (itemW * 5) - 1, bottom,
-                    mTextBackgroundPaint);
-            canvas.drawText(mText.clear()
-                    .append("Yv: ").append(ps.mYVelocity, 3)
-                    .toString(), 1 + itemW * 4, base, mTextPaint);
-
-            canvas.drawRect(itemW * 5, mHeaderPaddingTop, (itemW * 6) - 1, bottom,
-                    mTextBackgroundPaint);
-            canvas.drawRect(itemW * 5, mHeaderPaddingTop,
-                    (itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint);
-            canvas.drawText(mText.clear()
-                    .append("Prs: ").append(ps.mCoords.pressure, 2)
-                    .toString(), 1 + itemW * 5, base, mTextPaint);
-
-            canvas.drawRect(itemW * 6, mHeaderPaddingTop, w, bottom, mTextBackgroundPaint);
-            canvas.drawRect(itemW * 6, mHeaderPaddingTop,
-                    (itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint);
-            canvas.drawText(mText.clear()
-                    .append("Size: ").append(ps.mCoords.size, 2)
-                    .toString(), 1 + itemW * 6, base, mTextPaint);
-        }
+        drawLabels(canvas);
 
         // Pointer trace.
         for (int p = 0; p < NP; p++) {
@@ -463,6 +398,84 @@
         }
     }
 
+    private void drawLabels(Canvas canvas) {
+        if (mActivePointerId < 0) {
+            return;
+        }
+
+        final int w = getWidth() - mWaterfallInsets.left - mWaterfallInsets.right;
+        final int itemW = w / 7;
+        final int base = mHeaderPaddingTop - mTextMetrics.ascent + 1;
+        final int bottom = mHeaderBottom;
+
+        canvas.save();
+        canvas.translate(mWaterfallInsets.left, 0);
+        final PointerState ps = mPointers.get(mActivePointerId);
+
+        canvas.drawRect(0, mHeaderPaddingTop, itemW - 1, bottom, mTextBackgroundPaint);
+        canvas.drawText(mText.clear()
+                .append("P: ").append(mCurNumPointers)
+                .append(" / ").append(mMaxNumPointers)
+                .toString(), 1, base, mTextPaint);
+
+        final int count = ps.mTraceCount;
+        if ((mCurDown && ps.mCurDown) || count == 0) {
+            canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
+                    mTextBackgroundPaint);
+            canvas.drawText(mText.clear()
+                    .append("X: ").append(ps.mCoords.x, 1)
+                    .toString(), 1 + itemW, base, mTextPaint);
+            canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
+                    mTextBackgroundPaint);
+            canvas.drawText(mText.clear()
+                    .append("Y: ").append(ps.mCoords.y, 1)
+                    .toString(), 1 + itemW * 2, base, mTextPaint);
+        } else {
+            float dx = ps.mTraceX[count - 1] - ps.mTraceX[0];
+            float dy = ps.mTraceY[count - 1] - ps.mTraceY[0];
+            canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
+                    Math.abs(dx) < mVC.getScaledTouchSlop()
+                            ? mTextBackgroundPaint : mTextLevelPaint);
+            canvas.drawText(mText.clear()
+                    .append("dX: ").append(dx, 1)
+                    .toString(), 1 + itemW, base, mTextPaint);
+            canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
+                    Math.abs(dy) < mVC.getScaledTouchSlop()
+                            ? mTextBackgroundPaint : mTextLevelPaint);
+            canvas.drawText(mText.clear()
+                    .append("dY: ").append(dy, 1)
+                    .toString(), 1 + itemW * 2, base, mTextPaint);
+        }
+
+        canvas.drawRect(itemW * 3, mHeaderPaddingTop, (itemW * 4) - 1, bottom,
+                mTextBackgroundPaint);
+        canvas.drawText(mText.clear()
+                .append("Xv: ").append(ps.mXVelocity, 3)
+                .toString(), 1 + itemW * 3, base, mTextPaint);
+
+        canvas.drawRect(itemW * 4, mHeaderPaddingTop, (itemW * 5) - 1, bottom,
+                mTextBackgroundPaint);
+        canvas.drawText(mText.clear()
+                .append("Yv: ").append(ps.mYVelocity, 3)
+                .toString(), 1 + itemW * 4, base, mTextPaint);
+
+        canvas.drawRect(itemW * 5, mHeaderPaddingTop, (itemW * 6) - 1, bottom,
+                mTextBackgroundPaint);
+        canvas.drawRect(itemW * 5, mHeaderPaddingTop,
+                (itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint);
+        canvas.drawText(mText.clear()
+                .append("Prs: ").append(ps.mCoords.pressure, 2)
+                .toString(), 1 + itemW * 5, base, mTextPaint);
+
+        canvas.drawRect(itemW * 6, mHeaderPaddingTop, w, bottom, mTextBackgroundPaint);
+        canvas.drawRect(itemW * 6, mHeaderPaddingTop,
+                (itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint);
+        canvas.drawText(mText.clear()
+                .append("Size: ").append(ps.mCoords.size, 2)
+                .toString(), 1 + itemW * 6, base, mTextPaint);
+        canvas.restore();
+    }
+
     private void logMotionEvent(String type, MotionEvent event) {
         final int action = event.getAction();
         final int N = event.getHistorySize();
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 91a19e0..e274595 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -230,6 +230,7 @@
                 "libbinderthreadstateutils",
                 "libdmabufinfo",
                 "libgif",
+                "libgui_window_info_static",
                 "libseccomp_policy",
                 "libgrallocusage",
                 "libscrypt_static",
@@ -310,6 +311,7 @@
             header_libs: [
                 "bionic_libc_platform_headers",
                 "dnsproxyd_protocol_headers",
+                "libandroid_runtime_vm_headers",
             ],
         },
         host: {
@@ -369,6 +371,7 @@
                 "libinput",
                 "libbinderthreadstateutils",
                 "libsqlite",
+                "libgui_window_info_static",
             ],
             shared_libs: [
                 // libbinder needs to be shared since it has global state
@@ -384,3 +387,29 @@
         never: true,
     },
 }
+
+cc_library_headers {
+    name: "libandroid_runtime_vm_headers",
+    host_supported: true,
+    vendor_available: true,
+    // TODO(b/153609531): remove when libbinder is not native_bridge_supported
+    native_bridge_supported: true,
+    // Allow only modules from the following list to create threads that can be
+    // attached to the JVM. This list should be a subset of the dependencies of
+    // libandroid_runtime.
+    visibility: [
+        "//frameworks/native/libs/binder",
+    ],
+    export_include_dirs: ["include_vm"],
+    header_libs: [
+        "jni_headers",
+    ],
+    export_header_lib_headers: [
+        "jni_headers",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 406ccde..09d7ef0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -22,6 +22,7 @@
 #include <android-base/properties.h>
 #include <android/graphics/jni_runtime.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/vm.h>
 #include <assert.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
@@ -1331,6 +1332,10 @@
     return AndroidRuntime::mJavaVM;
 }
 
+extern "C" JavaVM* AndroidRuntimeGetJavaVM() {
+    return AndroidRuntime::getJavaVM();
+}
+
 /*
  * Get the JNIEnv pointer for this thread.
  *
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index ec99d6d..7eb7ac4 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -19,7 +19,7 @@
 
 #include <string>
 
-#include <input/InputApplication.h>
+#include <gui/InputApplication.h>
 
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 463d909..3a09562 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -26,14 +26,17 @@
 #include <ui/Region.h>
 #include <utils/threads.h>
 
+#include <gui/WindowInfo.h>
 #include "android_hardware_input_InputApplicationHandle.h"
 #include "android_util_Binder.h"
 #include "core_jni_helpers.h"
-#include "input/InputWindow.h"
 #include "jni.h"
 
 namespace android {
 
+using gui::TouchOcclusionMode;
+using gui::WindowInfo;
+
 struct WeakRefHandleField {
     jfieldID ctrl;
     jmethodID get;
@@ -66,7 +69,6 @@
     jfieldID packageName;
     jfieldID inputFeatures;
     jfieldID displayId;
-    jfieldID portalToDisplayId;
     jfieldID replaceTouchableRegionWithCrop;
     WeakRefHandleField touchableRegionSurfaceControl;
 } gInputWindowHandleClassInfo;
@@ -115,9 +117,9 @@
 
     mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
 
-    mInfo.flags = Flags<InputWindowInfo::Flag>(
+    mInfo.flags = Flags<WindowInfo::Flag>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
-    mInfo.type = static_cast<InputWindowInfo::Type>(
+    mInfo.type = static_cast<WindowInfo::Type>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
     mInfo.dispatchingTimeout = std::chrono::milliseconds(
             env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
@@ -159,12 +161,10 @@
     mInfo.ownerUid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerUid);
     mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
-    mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
+    mInfo.inputFeatures = static_cast<WindowInfo::Feature>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
     mInfo.displayId = env->GetIntField(obj,
             gInputWindowHandleClassInfo.displayId);
-    mInfo.portalToDisplayId = env->GetIntField(obj,
-            gInputWindowHandleClassInfo.portalToDisplayId);
 
     jobject inputApplicationHandleObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.inputApplicationHandle);
@@ -348,9 +348,6 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
             "displayId", "I");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
-            "portalToDisplayId", "I");
-
     GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
             "replaceTouchableRegionWithCrop", "Z");
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index de5bd6e..635480fc 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,14 +17,14 @@
 #ifndef _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
 #define _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
 
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 
 namespace android {
 
-class NativeInputWindowHandle : public InputWindowHandle {
+class NativeInputWindowHandle : public gui::WindowInfoHandle {
 public:
     NativeInputWindowHandle(jweak objWeak);
     virtual ~NativeInputWindowHandle();
@@ -37,7 +37,6 @@
     jweak mObjWeak;
 };
 
-
 extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
         JNIEnv* env, jobject inputWindowHandleObj);
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 16ee6a7..a6d038d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -28,6 +28,7 @@
 
 #include <android/media/AudioVibratorInfo.h>
 #include <audiomanager/AudioManager.h>
+#include <media/AudioContainers.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
 #include <media/MicrophoneInfo.h>
@@ -207,6 +208,7 @@
     jmethodID getId;
     jmethodID getResonantFrequency;
     jmethodID getQFactor;
+    jmethodID getMaxAmplitude;
 } gVibratorMethods;
 
 static Mutex gLock;
@@ -812,7 +814,8 @@
 static jint
 android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
 {
-    return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
+    return (jint)deviceTypesToBitMask(
+            AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(stream)));
 }
 
 static jint
@@ -2698,6 +2701,8 @@
         vibratorInfo.resonantFrequency =
                 env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getResonantFrequency);
         vibratorInfo.qFactor = env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getQFactor);
+        vibratorInfo.maxAmplitude =
+                env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude);
         vibratorInfos.push_back(vibratorInfo);
     }
     return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
@@ -3062,6 +3067,8 @@
     gVibratorMethods.getResonantFrequency =
             GetMethodIDOrDie(env, vibratorClass, "getResonantFrequency", "()F");
     gVibratorMethods.getQFactor = GetMethodIDOrDie(env, vibratorClass, "getQFactor", "()F");
+    gVibratorMethods.getMaxAmplitude =
+            GetMethodIDOrDie(env, vibratorClass, "getHapticChannelMaximumAmplitude", "()F");
 
     AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
 
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b40491a..f44e829 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,8 +50,7 @@
 }
 
 void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName,
-                         jstring devOptIn, jobjectArray featuresObj, jobject rulesFd,
-                         jlong rulesOffset, jlong rulesLength) {
+                         jstring devOptIn, jobjectArray featuresObj) {
     ScopedUtfChars pathChars(env, path);
     ScopedUtfChars appNameChars(env, appName);
     ScopedUtfChars devOptInChars(env, devOptIn);
@@ -74,11 +73,8 @@
         }
     }
 
-    int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
-
     android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
-                                                     devOptInChars.c_str(), features,
-                                                     rulesFd_native, rulesOffset, rulesLength);
+                                                     devOptInChars.c_str(), features);
 }
 
 bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
@@ -124,8 +120,7 @@
         {"setInjectLayersPrSetDumpable", "()Z",
          reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
         {"setAngleInfo",
-         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/io/"
-         "FileDescriptor;JJ)V",
+         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
          reinterpret_cast<void*>(setAngleInfo_native)},
         {"getShouldUseAngle", "(Ljava/lang/String;)Z",
          reinterpret_cast<void*>(shouldUseAngle_native)},
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 45e3d1b..16366a4 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -155,6 +155,7 @@
                                                    event->getYPrecision(),
                                                    event->getRawXCursorPosition(),
                                                    event->getRawYCursorPosition(),
+                                                   event->getDisplayOrientation(),
                                                    event->getDisplaySize().x,
                                                    event->getDisplaySize().y, event->getDownTime(),
                                                    event->getHistoricalEventTime(i),
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6971301..cabf3ab 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,6 +22,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <utils/Log.h>
@@ -56,6 +57,8 @@
     jfieldID toolMajor;
     jfieldID toolMinor;
     jfieldID orientation;
+    jfieldID relativeX;
+    jfieldID relativeY;
 } gPointerCoordsClassInfo;
 
 static struct {
@@ -212,6 +215,12 @@
             env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
     outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
             env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+                                      env->GetFloatField(pointerCoordsObj,
+                                                         gPointerCoordsClassInfo.relativeX));
+    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+                                      env->GetFloatField(pointerCoordsObj,
+                                                         gPointerCoordsClassInfo.relativeY));
 
     BitSet64 bits =
             BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
@@ -261,6 +270,12 @@
     float rawY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y);
     vec2 transformed = transform.transform(rawX, rawY);
 
+    float rawRelX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    float rawRelY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+    // Apply only rotation and scale, not translation.
+    const vec2 transformedOrigin = transform.transform(0, 0);
+    const vec2 transformedRel = transform.transform(rawRelX, rawRelY) - transformedOrigin;
+
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, transformed.x);
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, transformed.y);
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
@@ -277,6 +292,8 @@
             rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR));
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
             rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeX, transformedRel.x);
+    env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeY, transformedRel.y);
 
     uint64_t outBits = 0;
     BitSet64 bits = BitSet64(rawPointerCoords->bits);
@@ -289,6 +306,8 @@
     bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
     bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
     bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+    bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_X);
+    bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_Y);
     if (!bits.isEmpty()) {
         uint32_t packedAxesCount = bits.count();
         jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
@@ -378,8 +397,8 @@
                       flags, edgeFlags, metaState, buttonState,
                       static_cast<MotionClassification>(classification), transform, xPrecision,
                       yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                      AMOTION_EVENT_INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
+                      AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                      INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
                       pointerCount, pointerProperties, rawPointerCoords);
 
     return reinterpret_cast<jlong>(event.release());
@@ -872,6 +891,8 @@
     gPointerCoordsClassInfo.toolMajor = GetFieldIDOrDie(env, clazz, "toolMajor", "F");
     gPointerCoordsClassInfo.toolMinor = GetFieldIDOrDie(env, clazz, "toolMinor", "F");
     gPointerCoordsClassInfo.orientation = GetFieldIDOrDie(env, clazz, "orientation", "F");
+    gPointerCoordsClassInfo.relativeX = GetFieldIDOrDie(env, clazz, "relativeX", "F");
+    gPointerCoordsClassInfo.relativeY = GetFieldIDOrDie(env, clazz, "relativeY", "F");
 
     clazz = FindClassOrDie(env, "android/view/MotionEvent$PointerProperties");
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1695e1a..1955ec5 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -62,6 +62,8 @@
 
 namespace android {
 
+using gui::FocusRequest;
+
 static void doThrowNPE(JNIEnv* env) {
     jniThrowNullPointerException(env, NULL);
 }
@@ -882,8 +884,9 @@
 }
 
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
-    sp<IBinder> token =
-            SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
+    const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
+    if (!id) return nullptr;
+    sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
     return javaObjectForIBinder(env, token);
 }
 
@@ -1006,6 +1009,17 @@
     }
 }
 
+static void nativeSetDisplayFlags(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj,
+                                  jint flags) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return;
+
+    {
+        auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+        transaction->setDisplayFlags(token, flags);
+    }
+}
+
 static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
         jlong transactionObj,
         jobject tokenObj, jint orientation,
@@ -1881,6 +1895,8 @@
             (void*)nativeSetDisplaySurface },
     {"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
             (void*)nativeSetDisplayLayerStack },
+    {"nativeSetDisplayFlags", "(JLandroid/os/IBinder;I)V",
+            (void*)nativeSetDisplayFlags },
     {"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V",
             (void*)nativeSetDisplayProjection },
     {"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
diff --git a/core/jni/include_vm/android_runtime/vm.h b/core/jni/include_vm/android_runtime/vm.h
new file mode 100644
index 0000000..a6e7c16
--- /dev/null
+++ b/core/jni/include_vm/android_runtime/vm.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+// Get the Java VM. If the symbol doesn't exist at runtime, it means libandroid_runtime
+// is not loaded in the current process. If the symbol exists but it returns nullptr, it
+// means JavaVM is not yet started.
+extern "C" JavaVM* AndroidRuntimeGetJavaVM();
diff --git a/core/proto/android/server/OWNERS b/core/proto/android/server/OWNERS
new file mode 100644
index 0000000..72d39bf
--- /dev/null
+++ b/core/proto/android/server/OWNERS
@@ -0,0 +1 @@
+per-file window*.proto = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto
index 1fc4a01..41fecfd 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -46,17 +46,17 @@
     /* required: elapsed realtime in nanos since boot of when this entry was logged */
     optional fixed64 elapsed_realtime_nanos = 1;
     optional string calendar_time = 2;
-
-    optional string process_name = 3;
-    optional string thread_id_name = 4;
+    repeated string logging_type = 3;
+    optional string process_name = 4;
+    optional string thread_id_name = 5;
 
     /* where the trace originated */
-    optional string where = 5;
+    optional string where = 6;
 
-    optional string calling_pkg = 6;
-    optional string calling_params = 7;
-    optional string calling_stacks = 8;
+    optional string calling_pkg = 7;
+    optional string calling_params = 8;
+    optional string calling_stacks = 9;
 
-    optional AccessibilityDumpProto accessibility_service = 9;
-    optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10;
+    optional AccessibilityDumpProto accessibility_service = 10;
+    optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 11;
 }
diff --git a/core/proto/android/server/inputmethod/OWNERS b/core/proto/android/server/inputmethod/OWNERS
new file mode 100644
index 0000000..5deb2ce
--- /dev/null
+++ b/core/proto/android/server/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index fa1e9d4..0121bff 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -278,7 +278,7 @@
 message TaskProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional WindowContainerProto window_container = 1;
+    optional WindowContainerProto window_container = 1 [deprecated=true];
     optional int32 id = 2;
     reserved 3; // activity
     optional bool fills_parent = 4;
@@ -295,12 +295,12 @@
     optional string real_activity = 13;
     optional string orig_activity = 14;
 
-    optional int32 display_id = 15;
+    optional int32 display_id = 15 [deprecated=true];
     optional int32 root_task_id = 16;
-    optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+    optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType", deprecated=true] ;
     optional int32 resize_mode = 18 [(.android.typedef) = "android.appwidget.AppWidgetProviderInfo.ResizeModeFlags"];
-    optional int32 min_width = 19;
-    optional int32 min_height = 20;
+    optional int32 min_width = 19 [deprecated=true];
+    optional int32 min_height = 20 [deprecated=true];
 
     optional .android.graphics.RectProto adjusted_bounds = 21;
     optional .android.graphics.RectProto last_non_fullscreen_bounds = 22;
@@ -312,6 +312,18 @@
     optional bool created_by_organizer = 28;
     optional string affinity = 29;
     optional bool has_child_pip_activity = 30;
+    optional TaskFragmentProto task_fragment = 31;
+}
+
+/* represents TaskFragment */
+message TaskFragmentProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional WindowContainerProto window_container = 1;
+    optional int32 display_id = 2;
+    optional int32 activity_type = 3 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+    optional int32 min_width = 4;
+    optional int32 min_height = 5;
 }
 
 /* represents ActivityRecordProto */
@@ -493,6 +505,8 @@
     optional WindowTokenProto window_token = 7;
     /* represents a WindowState child */
     optional WindowStateProto window = 8;
+    /* represents a WindowState child */
+    optional TaskFragmentProto task_fragment = 9;
 }
 
 /* represents ConfigurationContainer */
diff --git a/core/proto/android/view/OWNERS b/core/proto/android/view/OWNERS
new file mode 100644
index 0000000..d72a0f0
--- /dev/null
+++ b/core/proto/android/view/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/wm/OWNERS
+
+per-file ime*.proto = file:/core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/view/inputmethod/OWNERS b/core/proto/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..5deb2ce
--- /dev/null
+++ b/core/proto/android/view/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4dec0ff..c6317cc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -202,6 +202,8 @@
     <protected-broadcast
         android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
@@ -2585,7 +2587,7 @@
          third-party apps.
     -->
     <permission android:name="android.permission.MANAGE_DOCUMENTS"
-        android:protectionLevel="signature|documenter" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows an application to manage access to crates, usually as part
          of a crates picker.
@@ -2602,7 +2604,7 @@
          <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.CACHE_CONTENT"
-        android:protectionLevel="signature|documenter" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide
          Allows an application to aggressively allocate disk space.
@@ -2748,7 +2750,7 @@
 
     <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
     <permission android:name="android.permission.REMOVE_TASKS"
-        android:protectionLevel="signature|documenter|recents" />
+        android:protectionLevel="signature|recents|role" />
 
     <!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
          @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 614779a..c6983ae 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -93,7 +93,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_marginEnd="@dimen/notification_header_separating_margin"
-                    android:ellipsize="marquee"
+                    android:ellipsize="end"
                     android:fadingEdge="horizontal"
                     android:singleLine="true"
                     android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 2991b17..95ddc2e 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -93,7 +93,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_marginEnd="@dimen/notification_header_separating_margin"
-                    android:ellipsize="marquee"
+                    android:ellipsize="end"
                     android:fadingEdge="horizontal"
                     android:singleLine="true"
                     android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 3564f97..bef1d0b 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -119,7 +119,7 @@
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:layout_marginEnd="@dimen/notification_header_separating_margin"
-                            android:ellipsize="marquee"
+                            android:ellipsize="end"
                             android:fadingEdge="horizontal"
                             android:singleLine="true"
                             android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 4920c79..7b834a4 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -21,7 +21,7 @@
     android:layout_height="@dimen/notification_text_height"
     android:layout_gravity="top"
     android:layout_marginTop="@dimen/notification_text_margin_top"
-    android:ellipsize="marquee"
+    android:ellipsize="end"
     android:fadingEdge="horizontal"
     android:gravity="top"
     android:singleLine="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 6946b3c..56d9a84 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
     <string name="selected" msgid="6614607926197755875">"gekies"</string>
     <string name="not_selected" msgid="410652016565864475">"nie gekies nie"</string>
+    <string name="in_progress" msgid="2149208189184319441">"aan die gang"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Voltooi handeling"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ab2f19..39a3d6e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
     <string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
     <string name="not_selected" msgid="410652016565864475">"አልተመረጠም"</string>
+    <string name="in_progress" msgid="2149208189184319441">"በሂደት ላይ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"እርምጃውን አጠናቅቅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf2f677..7bcd93e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1262,6 +1262,7 @@
     <string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
     <string name="selected" msgid="6614607926197755875">"محدّد"</string>
     <string name="not_selected" msgid="410652016565864475">"غير محدّد"</string>
+    <string name="in_progress" msgid="2149208189184319441">"قيد التقدّم"</string>
     <string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"‏إكمال الإجراء باستخدام %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"إكمال الإجراء"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 65e4664..a398c35 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
     <string name="selected" msgid="6614607926197755875">"বাছনি কৰা"</string>
     <string name="not_selected" msgid="410652016565864475">"বাছনি কৰা হোৱা নাই"</string>
+    <string name="in_progress" msgid="2149208189184319441">"চলি আছে"</string>
     <string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"কাৰ্য সম্পূৰ্ণ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index bab05ad..00145ba 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
     <string name="selected" msgid="6614607926197755875">"seçilib"</string>
     <string name="not_selected" msgid="410652016565864475">"seçilməyib"</string>
+    <string name="in_progress" msgid="2149208189184319441">"davam edir"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Əməliyyatı tamamlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4179ab0..65bfec1 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
     <string name="selected" msgid="6614607926197755875">"izabrano"</string>
     <string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
+    <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0970fc5..1fd1f18 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
     <string name="selected" msgid="6614607926197755875">"выбраны"</string>
     <string name="not_selected" msgid="410652016565864475">"не выбраны"</string>
+    <string name="in_progress" msgid="2149208189184319441">"выконваецца"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Завяршыць дзеянне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 40807e5..c74f488 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
     <string name="selected" msgid="6614607926197755875">"избрано"</string>
     <string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+    <string name="in_progress" msgid="2149208189184319441">"в ход"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Изпълняване на действието"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2f68cda..584fb61 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
     <string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string>
     <string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string>
+    <string name="in_progress" msgid="2149208189184319441">"কাজ চলছে"</string>
     <string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3ebb7cc..b6497ab 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
     <string name="selected" msgid="6614607926197755875">"odabrano"</string>
     <string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+    <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 880ad180..507fecd 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
     <string name="selected" msgid="6614607926197755875">"seleccionat"</string>
     <string name="not_selected" msgid="410652016565864475">"no seleccionat"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en curs"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Completa l\'acció"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 61fb471..2ca6c52 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
     <string name="selected" msgid="6614607926197755875">"vybráno"</string>
     <string name="not_selected" msgid="410652016565864475">"nevybráno"</string>
+    <string name="in_progress" msgid="2149208189184319441">"probíhá"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončit akci"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 45f02e3..0c26400 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
     <string name="selected" msgid="6614607926197755875">"valgt"</string>
     <string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+    <string name="in_progress" msgid="2149208189184319441">"i gang"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Afslut handling"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 542387f..ecb8070 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
     <string name="selected" msgid="6614607926197755875">"ausgewählt"</string>
     <string name="not_selected" msgid="410652016565864475">"nicht ausgewählt"</string>
+    <string name="in_progress" msgid="2149208189184319441">"Noch nicht abgeschlossen"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Abschließen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 67bd433..6fe7d63 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
     <string name="selected" msgid="6614607926197755875">"επιλεγμένο"</string>
     <string name="not_selected" msgid="410652016565864475">"μη επιλεγμένο"</string>
+    <string name="in_progress" msgid="2149208189184319441">"σε εξέλιξη"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Ολοκλήρωση ενέργειας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 141cf4e..bb26e6c 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
     <string name="selected" msgid="6614607926197755875">"selected"</string>
     <string name="not_selected" msgid="410652016565864475">"not selected"</string>
+    <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2b1d3e0..9dd378c 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"not checked"</string>
     <string name="selected" msgid="6614607926197755875">"selected"</string>
     <string name="not_selected" msgid="410652016565864475">"not selected"</string>
+    <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 01d8b39..81f49ce 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
     <string name="selected" msgid="6614607926197755875">"selected"</string>
     <string name="not_selected" msgid="410652016565864475">"not selected"</string>
+    <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 3ed0279..ddbcfb8 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
     <string name="selected" msgid="6614607926197755875">"selected"</string>
     <string name="not_selected" msgid="410652016565864475">"not selected"</string>
+    <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index a7ad2b6..996a332 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎not checked‎‏‎‎‏‎"</string>
     <string name="selected" msgid="6614607926197755875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎selected‎‏‎‎‏‎"</string>
     <string name="not_selected" msgid="410652016565864475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎not selected‎‏‎‎‏‎"</string>
+    <string name="in_progress" msgid="2149208189184319441">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎in progress‎‏‎‎‏‎"</string>
     <string name="whichApplication" msgid="5432266899591255759">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎Complete action using‎‏‎‎‏‎"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎Complete action using %1$s‎‏‎‎‏‎"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎Complete action‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5206c48..569cfcf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
     <string name="selected" msgid="6614607926197755875">"seleccionado"</string>
     <string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8c465f1..3ce014c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
     <string name="selected" msgid="6614607926197755875">"seleccionado"</string>
     <string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4dd7d94..6153a83 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
     <string name="selected" msgid="6614607926197755875">"valitud"</string>
     <string name="not_selected" msgid="410652016565864475">"pole valitud"</string>
+    <string name="in_progress" msgid="2149208189184319441">"pooleli"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Vii toiming lõpule"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d4705c1..a56d19a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
     <string name="selected" msgid="6614607926197755875">"hautatuta"</string>
     <string name="not_selected" msgid="410652016565864475">"hautatu gabe"</string>
+    <string name="in_progress" msgid="2149208189184319441">"abian"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Osatu ekintza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f388427..1c49d95 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
     <string name="selected" msgid="6614607926197755875">"انتخاب شده"</string>
     <string name="not_selected" msgid="410652016565864475">"انتخاب نشده"</string>
+    <string name="in_progress" msgid="2149208189184319441">"درحال انجام"</string>
     <string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5998269..5c7b4a7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
     <string name="selected" msgid="6614607926197755875">"valittu"</string>
     <string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
+    <string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 860e10b..83a9ac2 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"non coché"</string>
     <string name="selected" msgid="6614607926197755875">"sélectionné"</string>
     <string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 55fa145..4fec45b 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
     <string name="selected" msgid="6614607926197755875">"sélectionné"</string>
     <string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 76f54ff..cdbe604 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
     <string name="selected" msgid="6614607926197755875">"elemento seleccionado"</string>
     <string name="not_selected" msgid="410652016565864475">"elemento non seleccionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5add36b..3290919 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
     <string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string>
     <string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string>
+    <string name="in_progress" msgid="2149208189184319441">"પ્રક્રિયા ચાલુ છે"</string>
     <string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dcf357b..b5261e0 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
     <string name="selected" msgid="6614607926197755875">"चुना गया"</string>
     <string name="not_selected" msgid="410652016565864475">"नहीं चुना गया"</string>
+    <string name="in_progress" msgid="2149208189184319441">"जारी है"</string>
     <string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"कार्रवाई पूरी करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9aa3350..281cf85 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
     <string name="selected" msgid="6614607926197755875">"odabrano"</string>
     <string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+    <string name="in_progress" msgid="2149208189184319441">"u tijeku"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Dovrši radnju"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 908b779..9e0232d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
     <string name="selected" msgid="6614607926197755875">"kiválasztva"</string>
     <string name="not_selected" msgid="410652016565864475">"nincs kiválasztva"</string>
+    <string name="in_progress" msgid="2149208189184319441">"folyamatban"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Művelet végrehajtása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a7992f9..6fdf577 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
     <string name="selected" msgid="6614607926197755875">"ընտրված է"</string>
     <string name="not_selected" msgid="410652016565864475">"ընտրված չէ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ընթացքում է"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Ավարտել գործողությունը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 17382c4..df61452 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
     <string name="selected" msgid="6614607926197755875">"dipilih"</string>
     <string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+    <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 93e25a6..abd7735 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
     <string name="selected" msgid="6614607926197755875">"valið"</string>
     <string name="not_selected" msgid="410652016565864475">"ekki valið"</string>
+    <string name="in_progress" msgid="2149208189184319441">"í gangi"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Ljúka aðgerð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6919a3d..b35132e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
     <string name="selected" msgid="6614607926197755875">"selezionato"</string>
     <string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
+    <string name="in_progress" msgid="2149208189184319441">"In corso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Completa azione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 716e97c7..2daf5b1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
     <string name="selected" msgid="6614607926197755875">"נבחר"</string>
     <string name="not_selected" msgid="410652016565864475">"לא נבחר"</string>
+    <string name="in_progress" msgid="2149208189184319441">"בתהליך"</string>
     <string name="whichApplication" msgid="5432266899591255759">"השלמת הפעולה באמצעות"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"‏השלמת הפעולה באמצעות %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"להשלמת הפעולה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 30a62e3..1f9f4ab 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"OFF"</string>
     <string name="selected" msgid="6614607926197755875">"選択済み"</string>
     <string name="not_selected" msgid="410652016565864475">"未選択"</string>
+    <string name="in_progress" msgid="2149208189184319441">"進行中"</string>
     <string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"アクションを実行"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 8765659..09b3c2b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
     <string name="selected" msgid="6614607926197755875">"არჩეულია"</string>
     <string name="not_selected" msgid="410652016565864475">"არ არის არჩეული"</string>
+    <string name="in_progress" msgid="2149208189184319441">"მიმდინარეობს"</string>
     <string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"მოქმედების დასრულება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6e82813..d9fde8f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
     <string name="selected" msgid="6614607926197755875">"таңдалған"</string>
     <string name="not_selected" msgid="410652016565864475">"таңдалмаған"</string>
+    <string name="in_progress" msgid="2149208189184319441">"орындалуда"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Әрекетті аяқтау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d56a55d..716e810 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"មិន​បាន​ធីក​"</string>
     <string name="selected" msgid="6614607926197755875">"បាន​ជ្រើសរើស"</string>
     <string name="not_selected" msgid="410652016565864475">"មិនបានជ្រើសរើសទេ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ed2f964..4d328d3 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
     <string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
     <string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ea8fe228..a0054c0 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
     <string name="selected" msgid="6614607926197755875">"선택됨"</string>
     <string name="not_selected" msgid="410652016565864475">"선택되지 않음"</string>
+    <string name="in_progress" msgid="2149208189184319441">"진행 중"</string>
     <string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"작업 완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 4216086..45624a4 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
     <string name="selected" msgid="6614607926197755875">"тандалган"</string>
     <string name="not_selected" msgid="410652016565864475">"тандалган жок"</string>
+    <string name="in_progress" msgid="2149208189184319441">"аткарылууда"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index b1c0d59..7544c39 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
     <string name="selected" msgid="6614607926197755875">"ເລືອກແລ້ວ"</string>
     <string name="not_selected" msgid="410652016565864475">"ບໍ່ໄດ້ເລືອກແລ້ວ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ກຳລັງດຳເນີນການ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳ​ເລັດ​​​ການ​ດຳ​ເນີນ​ການ​ໂດຍ​ໃຊ້ %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ສຳເລັດຄຳສັ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1879dcc..18ac1b1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
     <string name="selected" msgid="6614607926197755875">"pasirinkta"</string>
     <string name="not_selected" msgid="410652016565864475">"nepasirinkta"</string>
+    <string name="in_progress" msgid="2149208189184319441">"vykdoma"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Užbaigti veiksmą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 15eda96..1799e38 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
     <string name="selected" msgid="6614607926197755875">"atlasīts"</string>
     <string name="not_selected" msgid="410652016565864475">"nav atlasīts"</string>
+    <string name="in_progress" msgid="2149208189184319441">"notiek apstrāde"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Pabeigt darbību"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b29d49e..39c3b8a 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
     <string name="selected" msgid="6614607926197755875">"избрано"</string>
     <string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+    <string name="in_progress" msgid="2149208189184319441">"во тек"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 44172ea..d6491b7 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
     <string name="selected" msgid="6614607926197755875">"തിരഞ്ഞെടുത്തു"</string>
     <string name="not_selected" msgid="410652016565864475">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+    <string name="in_progress" msgid="2149208189184319441">"പുരോഗതിയിലാണ്"</string>
     <string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4244015..e17b884 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
     <string name="selected" msgid="6614607926197755875">"сонгосон"</string>
     <string name="not_selected" msgid="410652016565864475">"сонгоогүй"</string>
+    <string name="in_progress" msgid="2149208189184319441">"үргэлжилж байна"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Үйлдлийг дуусгах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 28522d7..5195055 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
     <string name="selected" msgid="6614607926197755875">"निवडला"</string>
     <string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string>
+    <string name="in_progress" msgid="2149208189184319441">"प्रगतीपथावर आहे"</string>
     <string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 0bf24d7..76d52a9 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
     <string name="selected" msgid="6614607926197755875">"dipilih"</string>
     <string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+    <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 14409d8..74ecc1d 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
     <string name="selected" msgid="6614607926197755875">"ရွေးချယ်ထားသည်"</string>
     <string name="not_selected" msgid="410652016565864475">"ရွေးချယ်မထားပါ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ဆောင်ရွက်နေသည်"</string>
     <string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d046447..4394e49 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
     <string name="selected" msgid="6614607926197755875">"valgt"</string>
     <string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+    <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Fullfør handlingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 12f8cd4..07e4ffc 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
     <string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
     <string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string>
+    <string name="in_progress" msgid="2149208189184319441">"जारी छ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 75857ff..0c61054 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
     <string name="selected" msgid="6614607926197755875">"geselecteerd"</string>
     <string name="not_selected" msgid="410652016565864475">"niet geselecteerd"</string>
+    <string name="in_progress" msgid="2149208189184319441">"bezig"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Actie voltooien"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 11d6e7f..7d27d4e 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
     <string name="selected" msgid="6614607926197755875">"ଚୟନ କରାଯାଇଛି"</string>
     <string name="not_selected" msgid="410652016565864475">"ଚୟନ କରାଯାଇନାହିଁ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ଚାଲୁଅଛି"</string>
     <string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 2648296..12bf1bb 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string>
     <string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ਜਾਰੀ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c45ac41..2b8076a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
     <string name="selected" msgid="6614607926197755875">"wybrano"</string>
     <string name="not_selected" msgid="410652016565864475">"nie wybrano"</string>
+    <string name="in_progress" msgid="2149208189184319441">"w toku"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Wykonaj działanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 8198ae9..ab5f176 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
     <string name="selected" msgid="6614607926197755875">"selecionado"</string>
     <string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5a336ae..6455af0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
     <string name="selected" msgid="6614607926197755875">"selecionado"</string>
     <string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"em curso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8198ae9..ab5f176 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
     <string name="selected" msgid="6614607926197755875">"selecionado"</string>
     <string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+    <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ae0a629..c20a590 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
     <string name="selected" msgid="6614607926197755875">"selectat"</string>
     <string name="not_selected" msgid="410652016565864475">"neselectat"</string>
+    <string name="in_progress" msgid="2149208189184319441">"în curs"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8d3edf1..9c961ba 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
     <string name="selected" msgid="6614607926197755875">"выбрано"</string>
     <string name="not_selected" msgid="410652016565864475">"не выбрано"</string>
+    <string name="in_progress" msgid="2149208189184319441">"в процессе"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Выполнить действие"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 132ee6f..2b20e66 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
     <string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
     <string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
+    <string name="in_progress" msgid="2149208189184319441">"සිදු වෙමින් පවතී"</string>
     <string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්‍රියාව සම්පූර්ණ කරන්න"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්‍රියාව සම්පුර්ණ කරන්න"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ක්‍රියාව සම්පූර්ණ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 67b0ce0..cb18e2c 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
     <string name="selected" msgid="6614607926197755875">"vybrané"</string>
     <string name="not_selected" msgid="410652016565864475">"nevybrané"</string>
+    <string name="in_progress" msgid="2149208189184319441">"prebieha"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončiť akciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 93f2d742..b66d5f6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
     <string name="selected" msgid="6614607926197755875">"izbrano"</string>
     <string name="not_selected" msgid="410652016565864475">"ni izbrano"</string>
+    <string name="in_progress" msgid="2149208189184319441">"v teku"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 25e2820..bf04c0e 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
     <string name="selected" msgid="6614607926197755875">"i zgjedhur"</string>
     <string name="not_selected" msgid="410652016565864475">"i pazgjedhur"</string>
+    <string name="in_progress" msgid="2149208189184319441">"në vazhdim"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Përfundo veprimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index cd03394..5261bd3 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1202,6 +1202,7 @@
     <string name="not_checked" msgid="7972320087569023342">"није означено"</string>
     <string name="selected" msgid="6614607926197755875">"изабрано"</string>
     <string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
+    <string name="in_progress" msgid="2149208189184319441">"у току"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cb0b420..1b54981 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
     <string name="selected" msgid="6614607926197755875">"valt"</string>
     <string name="not_selected" msgid="410652016565864475">"inte valt"</string>
+    <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbee2c3..4166478 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
     <string name="selected" msgid="6614607926197755875">"imechaguliwa"</string>
     <string name="not_selected" msgid="410652016565864475">"haijachaguliwa"</string>
+    <string name="in_progress" msgid="2149208189184319441">"inaendelea"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Kamilisha kitendo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 18b588c..63a5805 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
     <string name="selected" msgid="6614607926197755875">"தேர்ந்தெடுக்கப்பட்டது"</string>
     <string name="not_selected" msgid="410652016565864475">"தேர்ந்தெடுக்கப்படவில்லை"</string>
+    <string name="in_progress" msgid="2149208189184319441">"செயலிலுள்ளது"</string>
     <string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"செயலை முடி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d68c088..01d0226 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
     <string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string>
     <string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string>
+    <string name="in_progress" msgid="2149208189184319441">"ప్రోగ్రెస్‌లో ఉంది"</string>
     <string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string>
diff --git a/core/res/res/values-television/strings.xml b/core/res/res/values-television/strings.xml
new file mode 100644
index 0000000..37b1049
--- /dev/null
+++ b/core/res/res/values-television/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!--- Title of notification triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_mic_notification_content_title">Microphone is blocked</string>
+    <!--- Title of notification triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+    <string name="sensor_privacy_start_use_camera_notification_content_title">Camera is blocked</string>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 379830a..44516d7 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
     <string name="selected" msgid="6614607926197755875">"เลือกไว้"</string>
     <string name="not_selected" msgid="410652016565864475">"ไม่ได้เลือกไว้"</string>
+    <string name="in_progress" msgid="2149208189184319441">"กำลังดำเนินการ"</string>
     <string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"ทำงานให้เสร็จสิ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 73848a3..a21c4f0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
     <string name="selected" msgid="6614607926197755875">"pinili"</string>
     <string name="not_selected" msgid="410652016565864475">"hindi pinili"</string>
+    <string name="in_progress" msgid="2149208189184319441">"isinasagawa"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Gawin ang pagkilos"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3325e7e..deda317 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
     <string name="selected" msgid="6614607926197755875">"seçili"</string>
     <string name="not_selected" msgid="410652016565864475">"seçili değil"</string>
+    <string name="in_progress" msgid="2149208189184319441">"devam ediyor"</string>
     <string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"İşlemi tamamla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 4912be8..fe64d9d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1222,6 +1222,7 @@
     <string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
     <string name="selected" msgid="6614607926197755875">"вибрано"</string>
     <string name="not_selected" msgid="410652016565864475">"не вибрано"</string>
+    <string name="in_progress" msgid="2149208189184319441">"триває"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3f9cee1..78758e8 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
     <string name="selected" msgid="6614607926197755875">"منتخب کردہ"</string>
     <string name="not_selected" msgid="410652016565864475">"غیر منتخب کردہ"</string>
+    <string name="in_progress" msgid="2149208189184319441">"جاری ہے"</string>
     <string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"‏%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"کارروائی مکمل کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index df7d467..ecc5f52 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
     <string name="selected" msgid="6614607926197755875">"tanlangan"</string>
     <string name="not_selected" msgid="410652016565864475">"tanlanmagan"</string>
+    <string name="in_progress" msgid="2149208189184319441">"bajarilmoqda"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 259696d..38a424b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
     <string name="selected" msgid="6614607926197755875">"đã chọn"</string>
     <string name="not_selected" msgid="410652016565864475">"chưa được chọn"</string>
+    <string name="in_progress" msgid="2149208189184319441">"đang thực hiện"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 68a5ce7..ea66619 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
     <string name="selected" msgid="6614607926197755875">"已选择"</string>
     <string name="not_selected" msgid="410652016565864475">"未选择"</string>
+    <string name="in_progress" msgid="2149208189184319441">"进行中"</string>
     <string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fa2fee5..ccfb026 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
     <string name="selected" msgid="6614607926197755875">"揀咗"</string>
     <string name="not_selected" msgid="410652016565864475">"未揀"</string>
+    <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
     <string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 798e06c..d6c98c5 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
     <string name="selected" msgid="6614607926197755875">"已選取"</string>
     <string name="not_selected" msgid="410652016565864475">"未選取"</string>
+    <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
     <string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f93b844..9c9d68a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1182,6 +1182,7 @@
     <string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
     <string name="selected" msgid="6614607926197755875">"okukhethiwe"</string>
     <string name="not_selected" msgid="410652016565864475">"akukhethiwe"</string>
+    <string name="in_progress" msgid="2149208189184319441">"kuyaqhubeka"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Qedela isenzo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d052d70..73dfab6 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -285,9 +285,6 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system default text classifier -->
         <flag name="textClassifier" value="0x10000" />
-        <!-- Additional flag from base permission type: this permission can be automatically
-            granted to the document manager -->
-        <flag name="documenter" value="0x40000" />
         <!-- Additional flag from base permission type: this permission automatically
             granted to device configurator -->
         <flag name="configurator" value="0x80000" />
@@ -1193,7 +1190,7 @@
          <code>dalvik.system.DelegateLastClassLoader</code>. If unspecified,
          the default value of this attribute is <code>dalvik.system.PathClassLoader</code>.
 
-         If an unknown classloader is provided, a PackageParserException with cause
+         If an unknown classloader is provided, a PackageManagerException with cause
          <code>PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED</code> will be
          thrown and the app will not be installed.
          -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index db43b5b..f99cb19 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -657,6 +657,9 @@
     <!-- Indicate the display area rect for foldable devices in folded state. -->
     <string name="config_foldedArea"></string>
 
+    <!-- Indicates whether to enable an animation when unfolding a device or not -->
+    <bool name="config_unfoldTransitionEnabled">false</bool>
+
     <!-- Indicates that the device supports having more than one internal display on at the same
          time. Only applicable to devices with more than one internal display. If this option is
          set to false, DisplayManager will make additional effort to ensure no more than 1 internal
@@ -3607,8 +3610,8 @@
     -->
     <integer name="config_largeScreenSmallestScreenWidthDp">600</integer>
 
-    <!-- True if the device is using leagacy split. -->
-    <bool name="config_useLegacySplit">true</bool>
+    <!-- True if the device is using legacy split. -->
+    <bool name="config_useLegacySplit">false</bool>
 
     <!-- True if the device supports running activities on secondary displays. -->
     <bool name="config_supportsMultiDisplay">true</bool>
@@ -3740,6 +3743,7 @@
     <string-array translatable="false" name="config_nonBlockableNotificationPackages">
         <item>com.android.dialer</item>
         <item>com.android.messaging</item>
+        <item>com.android.cellbroadcastreceiver.module</item>
     </string-array>
 
     <!-- An array of packages that can make sound on the ringer stream in priority-only DND
@@ -4739,9 +4743,15 @@
          MediaSessionService. -->
     <string name="config_customMediaSessionPolicyProvider"></string>
 
+    <!-- The min scale for the wallpaper when it's zoomed out -->
+    <item name="config_wallpaperMinScale" format="float" type="dimen">1</item>
+
     <!-- The max scale for the wallpaper when it's zoomed in -->
     <item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
 
+    <!-- If true, the wallpaper will scale regardless of the value of shouldZoomOutWallpaper() -->
+    <bool name="config_alwaysScaleWallpaper">false</bool>
+
     <!-- Package name that will receive an explicit manifest broadcast for
          android.os.action.POWER_SAVE_MODE_CHANGED. -->
     <string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
@@ -4760,6 +4770,16 @@
     <!-- pdp data reject retry delay in ms -->
     <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
 
+    <!-- Duration in milliseconds for device to vibrate on mash press on power
+         button. -->
+    <integer name="config_mashPressVibrateTimeOnPowerButton">0</integer>
+
+    <!-- Control the behavior when the user presses the power button 5 times.
+           0 - Nothing
+           1 - Launch panic button gesture
+    -->
+    <integer name="config_mashPressOnPowerBehavior">0</integer>
+
     <!-- Whether or not to enable the binder heavy hitter watcher by default -->
     <bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
 
@@ -4902,13 +4922,21 @@
 
     <bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
     <bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
-    <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+    <bool name="config_cecHdmiCecVersion14b_default">false</bool>
     <bool name="config_cecHdmiCecVersion20_allowed">true</bool>
-    <bool name="config_cecHdmiCecVersion20_default">false</bool>
+    <bool name="config_cecHdmiCecVersion20_default">true</bool>
+
+    <bool name="config_cecRoutingControl_userConfigurable">true</bool>
+    <bool name="config_cecRoutingControlEnabled_allowed">true</bool>
+    <bool name="config_cecRoutingControlEnabled_default">false</bool>
+    <bool name="config_cecRoutingControlDisabled_allowed">true</bool>
+    <bool name="config_cecRoutingControlDisabled_default">true</bool>
 
     <bool name="config_cecPowerControlMode_userConfigurable">true</bool>
     <bool name="config_cecPowerControlModeTv_allowed">true</bool>
-    <bool name="config_cecPowerControlModeTv_default">true</bool>
+    <bool name="config_cecPowerControlModeTv_default">false</bool>
+    <bool name="config_cecPowerControlModeTvAndAudioSystem_allowed">true</bool>
+    <bool name="config_cecPowerControlModeTvAndAudioSystem_default">true</bool>
     <bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
     <bool name="config_cecPowerControlModeBroadcast_default">false</bool>
     <bool name="config_cecPowerControlModeNone_allowed">true</bool>
@@ -4920,6 +4948,12 @@
     <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
     <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
 
+    <bool name="config_cecSystemAudioControl_userConfigurable">true</bool>
+    <bool name="config_cecSystemAudioControlEnabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioControlEnabled_default">true</bool>
+    <bool name="config_cecSystemAudioControlDisabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioControlDisabled_default">false</bool>
+
     <bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
     <bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
     <bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
@@ -5044,4 +5078,7 @@
 
     <!-- the number of the max cached processes in the system. -->
     <integer name="config_customizedMaxCachedProcesses">32</integer>
+
+    <!-- Whether this device should support taking app snapshots on closure -->
+    <bool name="config_disableTaskSnapshots">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2403a60..a519cde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3201,10 +3201,158 @@
     <public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" />
 
   <!-- ===============================================================
+    Resources added in version S-V2 of the platform
+
+    NOTE: add <public> elements within a <staging-public-group> like so:
+
+    <staging-public-group type="attr" first-id="0x01ff0000">
+        <public name="exampleAttr1" />
+        <public name="exampleAttr2" />
+    </staging-public-group>
+
+    To add a new <staging-public-group> block, find the id value for the
+    last <staging-public-group> block defined for thie API level, and
+    subtract 0x00010000 from it to get to the id of the new block.
+
+    For example, if the block closest to the end of this file has an id
+    of 0x01ee0000, the id of the new block should be 0x01ed0000
+    (0x01ee0000 - 0x00010000 = 0x01ed0000).
+    =============================================================== -->
+  <eat-comment />
+
+  <staging-public-group type="attr" first-id="0x01ff0000">
+  </staging-public-group>
+
+  <staging-public-group type="id" first-id="0x01fe0000">
+  </staging-public-group>
+
+  <staging-public-group type="style" first-id="0x01fd0000">
+  </staging-public-group>
+
+  <staging-public-group type="string" first-id="0x01fc0000">
+  </staging-public-group>
+
+  <staging-public-group type="dimen" first-id="0x01fb0000">
+  </staging-public-group>
+
+  <staging-public-group type="color" first-id="0x01fa0000">
+  </staging-public-group>
+
+  <staging-public-group type="array" first-id="0x01f90000">
+  </staging-public-group>
+
+  <staging-public-group type="drawable" first-id="0x01f80000">
+  </staging-public-group>
+
+  <staging-public-group type="layout" first-id="0x01f70000">
+  </staging-public-group>
+
+  <staging-public-group type="anim" first-id="0x01f60000">
+  </staging-public-group>
+
+  <staging-public-group type="animator" first-id="0x01f50000">
+  </staging-public-group>
+
+  <staging-public-group type="interpolator" first-id="0x01f40000">
+  </staging-public-group>
+
+  <staging-public-group type="mipmap" first-id="0x01f30000">
+  </staging-public-group>
+
+  <staging-public-group type="integer" first-id="0x01f20000">
+  </staging-public-group>
+
+  <staging-public-group type="transition" first-id="0x01f10000">
+  </staging-public-group>
+
+  <staging-public-group type="raw" first-id="0x01f00000">
+  </staging-public-group>
+
+  <staging-public-group type="bool" first-id="0x01ef0000">
+  </staging-public-group>
+
+  <staging-public-group type="fraction" first-id="0x01ee0000">
+  </staging-public-group>
+
+  <!-- ===============================================================
+    Resources added in version T of the platform
+
+    NOTE: add <public> elements within a <staging-public-group> like so:
+
+    <staging-public-group type="attr" first-id="0x01ff0000">
+        <public name="exampleAttr1" />
+        <public name="exampleAttr2" />
+    </staging-public-group>
+
+    To add a new <staging-public-group> block, find the id value for the
+    last <staging-public-group> block defined for thie API level, and
+    subtract 0x00010000 from it to get to the id of the new block.
+
+    For example, if the block closest to the end of this file has an id
+    of 0x01ee0000, the id of the new block should be 0x01ed0000
+    (0x01ee0000 - 0x00010000 = 0x01ed0000).
+    =============================================================== -->
+  <eat-comment />
+
+  <staging-public-group type="attr" first-id="0x01df0000">
+  </staging-public-group>
+
+  <staging-public-group type="id" first-id="0x01de0000">
+  </staging-public-group>
+
+  <staging-public-group type="style" first-id="0x0dfd0000">
+  </staging-public-group>
+
+  <staging-public-group type="string" first-id="0x0dfc0000">
+  </staging-public-group>
+
+  <staging-public-group type="dimen" first-id="0x01db0000">
+  </staging-public-group>
+
+  <staging-public-group type="color" first-id="0x01da0000">
+  </staging-public-group>
+
+  <staging-public-group type="array" first-id="0x01d90000">
+  </staging-public-group>
+
+  <staging-public-group type="drawable" first-id="0x01d80000">
+  </staging-public-group>
+
+  <staging-public-group type="layout" first-id="0x01d70000">
+  </staging-public-group>
+
+  <staging-public-group type="anim" first-id="0x01d60000">
+  </staging-public-group>
+
+  <staging-public-group type="animator" first-id="0x01d50000">
+  </staging-public-group>
+
+  <staging-public-group type="interpolator" first-id="0x01d40000">
+  </staging-public-group>
+
+  <staging-public-group type="mipmap" first-id="0x01d30000">
+  </staging-public-group>
+
+  <staging-public-group type="integer" first-id="0x01d20000">
+  </staging-public-group>
+
+  <staging-public-group type="transition" first-id="0x01d10000">
+  </staging-public-group>
+
+  <staging-public-group type="raw" first-id="0x01d00000">
+  </staging-public-group>
+
+  <staging-public-group type="bool" first-id="0x01cf0000">
+  </staging-public-group>
+
+  <staging-public-group type="fraction" first-id="0x01ce0000">
+  </staging-public-group>
+
+  <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
        Any new items (attrs, styles, ids, etc.) *must* be added in a
-       public-group block, as the preceding comment explains.
+       staging-public-group block, as the preceding comment explains.
        Items added outside of a group may have their value recalculated
        every time something new is added to this file.
        =============================================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a99a220..d2c6f78 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3195,6 +3195,9 @@
     <!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
     <string name="not_selected">not selected</string>
 
+    <!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
+    <string name="in_progress">in progress</string>
+
     <!-- Title of intent resolver dialog when selecting an application to run. -->
     <string name="whichApplication">Complete action using</string>
     <!-- Title of intent resolver dialog when selecting an application to run
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index adb046e..1084963 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -798,6 +798,7 @@
   <java-symbol type="string" name="ime_action_previous" />
   <java-symbol type="string" name="ime_action_search" />
   <java-symbol type="string" name="ime_action_send" />
+  <java-symbol type="string" name="in_progress" />
   <java-symbol type="string" name="invalidPin" />
   <java-symbol type="string" name="js_dialog_before_unload_positive_button" />
   <java-symbol type="string" name="js_dialog_before_unload_negative_button" />
@@ -3837,6 +3838,7 @@
   <java-symbol type="array" name="config_foldedDeviceStates" />
   <java-symbol type="string" name="config_foldedArea" />
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
+  <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
 
   <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
   <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
@@ -4312,9 +4314,17 @@
   <java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
   <java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
 
+  <java-symbol type="bool" name="config_cecRoutingControl_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRoutingControlEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecRoutingControlEnabled_default" />
+  <java-symbol type="bool" name="config_cecRoutingControlDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecRoutingControlDisabled_default" />
+
   <java-symbol type="bool" name="config_cecPowerControlMode_userConfigurable" />
   <java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
   <java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_default" />
   <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
   <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
   <java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
@@ -4326,6 +4336,12 @@
   <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
   <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
 
+  <java-symbol type="bool" name="config_cecSystemAudioControl_userConfigurable" />
+  <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_default" />
+  <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_default" />
+
   <java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
   <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
   <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
@@ -4425,4 +4441,6 @@
   <java-symbol type="bool" name="config_volumeShowRemoteSessions" />
 
   <java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
+
+  <java-symbol type="bool" name="config_disableTaskSnapshots" />
 </resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 873b9eb..5ad3748 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,18 +18,18 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="9">
-    <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
-            reminders="false" events="false" repeatCallers="true" convos="false"
-            convosFrom="3"/>
+<zen version="10">
+    <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="true"
+            messagesFrom="2" reminders="false" events="false" repeatCallers="true" convos="true"
+            convosFrom="2"/>
     <automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
                component="android/com.android.server.notification.EventConditionProvider"
                conditionId="condition://android/event?userId=-10000&amp;calendar=&amp;reply=1"/>
     <automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping"
                zen="1" component="android/com.android.server.notification.ScheduleConditionProvider"
                conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&amp;start=22.0&amp;end=7.0&amp;exitAtAlarm=true"/>
-    <!-- all visual effects that exist as of P -->
-    <disallow visualEffects="511" />
+    <!-- everything when screen off (for old target sdks); fullscreen; lights; peek; ambient -->
+    <disallow visualEffects="157" />
     <!-- whether there are notification channels that can bypass dnd -->
     <state areChannelsBypassingDnd="false" />
 </zen>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2650d9f..2be5c47 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -245,6 +245,9 @@
     <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
     <shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
 
+    <!-- Taiwan -->
+    <shortcode country="tw" pattern="\\d{4}" free="1922" />
+
     <!-- Thailand: 4186001 used by AIS_TH_DCB -->
     <shortcode country="th" pattern="\\d{1,5}" premium="4\\d{6}" free="4186001" />
 
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
index c65ef9a..53ba140 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
@@ -16,18 +16,40 @@
 
 package android.accessibilityservice;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowTokenClient;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,6 +64,8 @@
 public class AccessibilityServiceTest {
     private static final String TAG = "AccessibilityServiceTest";
     private static final int CONNECTION_ID = 1;
+    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
+            TYPE_ACCESSIBILITY_OVERLAY);
 
     private static class AccessibilityServiceTestClass extends AccessibilityService {
         private IAccessibilityServiceClient mCallback;
@@ -49,7 +73,11 @@
 
         AccessibilityServiceTestClass() {
             super();
-            attachBaseContext(InstrumentationRegistry.getContext());
+            Context context = ApplicationProvider.getApplicationContext();
+            final Display display = context.getSystemService(DisplayManager.class)
+                    .getDisplay(DEFAULT_DISPLAY);
+
+            attachBaseContext(context.createTokenContext(new WindowTokenClient(), display));
             mLooper = InstrumentationRegistry.getContext().getMainLooper();
         }
 
@@ -78,14 +106,33 @@
     private @Mock IBinder mMockIBinder;
     private IAccessibilityServiceClient mServiceInterface;
     private AccessibilityServiceTestClass mService;
+    private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
 
     @Before
-    public void setUp() throws RemoteException {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mService = new AccessibilityServiceTestClass();
+        mService.onCreate();
         mService.setupCallback(mMockClientForCallback);
         mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
         mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
+        doAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            final int displayId = (int) args[0];
+            final IBinder token = new Binder();
+            WindowManagerGlobal.getWindowManagerService().addWindowToken(token,
+                    TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */);
+            mWindowTokens.put(displayId, token);
+            return token;
+        }).when(mMockConnection).getOverlayWindowToken(anyInt());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (int i = mWindowTokens.size() - 1; i >= 0; --i) {
+            WindowManagerGlobal.getWindowManagerService().removeWindowToken(
+                    mWindowTokens.valueAt(i), mWindowTokens.keyAt(i));
+        }
     }
 
     @Test
@@ -101,4 +148,79 @@
 
         verify(mMockConnection).getSystemActions();
     }
+
+    @Test
+    public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception {
+        try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+            final Context context = mService.createDisplayContext(session.getDisplay());
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                    () -> context.getSystemService(WindowManager.class)
+                            .addView(new View(context), mParams)
+            );
+        }
+    }
+
+    @Test
+    public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception {
+        try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+            final Context context = mService.createDisplayContext(session.getDisplay())
+                    .createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                    () -> context.getSystemService(WindowManager.class)
+                            .addView(new View(context), mParams)
+            );
+        }
+    }
+
+    @Test
+    public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception {
+        try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+            final Context context = mService.createWindowContext(session.getDisplay(),
+                    TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                    () -> context.getSystemService(WindowManager.class)
+                            .addView(new View(context), mParams)
+            );
+        }
+    }
+
+    @Test(expected = WindowManager.BadTokenException.class)
+    public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType()
+            throws Exception {
+        try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+            final Context context = mService.createWindowContext(session.getDisplay(),
+                    TYPE_APPLICATION_OVERLAY, null /* options */);
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                    () -> context.getSystemService(WindowManager.class)
+                            .addView(new View(context), mParams)
+            );
+        }
+    }
+
+
+    private static class VirtualDisplaySession implements AutoCloseable {
+        private final VirtualDisplay mVirtualDisplay;
+
+        VirtualDisplaySession() {
+            final DisplayManager displayManager = ApplicationProvider.getApplicationContext()
+                    .getSystemService(DisplayManager.class);
+            final int width = 800;
+            final int height = 480;
+            final int density = 160;
+            ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+                    2 /* maxImages */);
+            mVirtualDisplay = displayManager.createVirtualDisplay(
+                    TAG, width, height, density, reader.getSurface(),
+                    VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+        }
+
+        private Display getDisplay() {
+            return mVirtualDisplay.getDisplay();
+        }
+
+        @Override
+        public void close() throws Exception {
+            mVirtualDisplay.release();
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 4b0ed65..2c3c1ed 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -26,8 +26,11 @@
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.LargeTest;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import junit.framework.TestCase;
 
 import org.mockito.Mockito;
@@ -110,6 +113,13 @@
         public boolean isAllow3rdPartyOnInternal(Context context) {
             return mAllow3rdPartyOnInternal;
         }
+
+        @Override
+        @VisibleForTesting
+        protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app,
+                StorageManager storageManager, IPackageManager pm) {
+            return super.getPackageCandidateVolumes(app, storageManager, pm);
+        }
     }
 
     private StorageManager getMockedStorageManager() {
@@ -223,7 +233,7 @@
             appInfo.flags = 0;
 
             appInfo.volumeUuid = sInternalVolUuid;
-            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
+            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(false);
             appPkgMgr.setAllow3rdPartyOnInternal(true);
             List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
                     appInfo, storageManager, pm);
@@ -231,7 +241,7 @@
 
             appInfo.volumeUuid = sInternalVolUuid;
             appPkgMgr.setAllow3rdPartyOnInternal(true);
-            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true);
+            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(true);
             candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
             verifyReturnedVolumes(candidates, sInternalVol);
 
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 8c05978..7a2c63f 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -20,6 +20,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -57,8 +58,70 @@
         }
     }
 
+    // Clear the test mode after every test, in case this process is used for other tests.
+    @After
+    public void tearDown() throws Exception {
+        PropertyInvalidatedCache.setTestMode(false);
+    }
+
+    // This test is disabled pending an sepolicy change that allows any app to set the
+    // test property.
     @Test
-    public void testDisableCache1() {
+    public void testBasicCache() {
+
+        // A stand-in for the binder.  The test verifies that calls are passed through to
+        // this class properly.
+        ServerProxy tester = new ServerProxy();
+
+        // Create a cache that uses simple arithmetic to computer its values.
+        PropertyInvalidatedCache<Integer, Boolean> testCache =
+                new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+                    @Override
+                    protected Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                    @Override
+                    protected boolean bypass(Integer x) {
+                        return x % 13 == 0;
+                    }
+                };
+
+        PropertyInvalidatedCache.setTestMode(true);
+        PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY);
+
+        tester.verify(0);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(1);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(2);
+        testCache.invalidateCache();
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(3);
+        assertEquals(tester.value(5), testCache.query(5));
+        tester.verify(4);
+        assertEquals(tester.value(5), testCache.query(5));
+        tester.verify(4);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(4);
+
+        // Invalidate the cache, and verify that the next read on 3 goes to the server.
+        testCache.invalidateCache();
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(5);
+
+        // Test bypass.  The query for 13 always bypasses the cache.
+        assertEquals(tester.value(12), testCache.query(12));
+        assertEquals(tester.value(13), testCache.query(13));
+        assertEquals(tester.value(14), testCache.query(14));
+        tester.verify(8);
+        assertEquals(tester.value(12), testCache.query(12));
+        assertEquals(tester.value(13), testCache.query(13));
+        assertEquals(tester.value(14), testCache.query(14));
+        tester.verify(9);
+    }
+
+    @Test
+    public void testDisableCache() {
 
         // A stand-in for the binder.  The test verifies that calls are passed through to
         // this class properly.
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index d1776fb..3d7d807 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -32,7 +32,6 @@
 import android.graphics.PixelFormat;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
-import android.inputmethodservice.InputMethodService;
 import android.media.ImageReader;
 import android.os.UserHandle;
 import android.view.Display;
@@ -140,13 +139,6 @@
     }
 
     @Test
-    public void testIsUiContext_InputMethodService_returnsTrue() {
-        final InputMethodService ims = new InputMethodService();
-
-        assertTrue(ims.isUiContext());
-    }
-
-    @Test
     public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
         verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
     }
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 5d75d9b..7f53776 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -34,7 +34,6 @@
 import android.content.IntentSender;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
@@ -574,7 +573,7 @@
 
         ParsingPackage pkg;
 
-        InstallParams(String outFileName, int rawResId) throws PackageParserException {
+        InstallParams(String outFileName, int rawResId) {
             this.pkg = getParsedPackage(outFileName, rawResId);
             this.packageURI = Uri.fromFile(new File(pkg.getPath()));
         }
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 49b720c..cf7e5c66 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,19 +15,18 @@
  */
 package android.content.pm;
 
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.AUTH;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.PERMISSION;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.ROLLBACK;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
+import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
+import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
+import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
+import static android.content.pm.SigningDetails.CertCapabilities.ROLLBACK;
+import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.content.pm.PackageParser.SigningDetails;
 import android.util.ArraySet;
 import android.util.PackageUtils;
 
@@ -990,11 +989,11 @@
     private void assertSigningDetailsContainsLineage(SigningDetails details,
             String... pastSigners) {
         // This method should only be invoked for results that contain a single signer.
-        assertEquals(1, details.signatures.length);
-        assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+        assertEquals(1, details.getSignatures().length);
+        assertTrue(details.getSignatures()[0].toCharsString().equalsIgnoreCase(
                 pastSigners[pastSigners.length - 1]));
         Set<String> signatures = new ArraySet<>(pastSigners);
-        for (Signature pastSignature : details.pastSigningCertificates) {
+        for (Signature pastSignature : details.getPastSigningCertificates()) {
             assertTrue(signatures.remove(pastSignature.toCharsString()));
         }
         assertEquals(0, signatures.size());
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index 22f6ec0..701e619 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -27,6 +27,7 @@
 
 import static junit.framework.Assert.fail;
 
+import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontStyle;
 import android.os.LocaleList;
 import android.text.FontConfig;
@@ -46,6 +47,7 @@
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -318,6 +320,52 @@
         }
     }
 
+    @Test
+    public void alias() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font>test.ttf</font>"
+                + "  </family>"
+                + "  <family name='custom-family'>"
+                + "    <font>missing.ttf</font>"
+                + "  </family>"
+                + "  <alias name='custom-alias' to='sans-serif'/>"
+                + "</familyset>";
+        FontConfig config = readFamilies(xml, true /* include non-existing font files */);
+        List<FontConfig.Alias> aliases = config.getAliases();
+        assertThat(aliases.size()).isEqualTo(1);
+        assertThat(aliases.get(0).getName()).isEqualTo("custom-alias");
+        assertThat(aliases.get(0).getOriginal()).isEqualTo("sans-serif");
+    }
+
+    @Test
+    public void dropped_FamilyAlias() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font>test.ttf</font>"
+                + "  </family>"
+                + "  <family name='custom-family'>"
+                + "    <font>missing.ttf</font>"
+                + "  </family>"
+                + "  <alias name='custom-alias' to='custom-family'/>"
+                + "</familyset>";
+        FontConfig config = readFamilies(xml, false /* exclude not existing file */);
+        assertThat(config.getAliases()).isEmpty();
+    }
+
+    private FontConfig readFamilies(String xml, boolean allowNonExisting)
+            throws IOException, XmlPullParserException {
+        ByteArrayInputStream buffer = new ByteArrayInputStream(
+                xml.getBytes(StandardCharsets.UTF_8));
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(buffer, "UTF-8");
+        parser.nextTag();
+        return FontListParser.readFamilies(parser, "", new FontCustomizationParser.Result(), null,
+                0L /* last modified date */, 0 /* config version */, allowNonExisting);
+    }
+
     private FontConfig.FontFamily readFamily(String xml)
             throws IOException, XmlPullParserException {
         ByteArrayInputStream buffer = new ByteArrayInputStream(
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cde..fd7753b 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -773,46 +773,51 @@
 
         try {
             // Ensure the device starts in a known state.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
 
             // Assert starting state.
-            assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+            assertThat(DeviceConfig.getSyncDisabledMode())
+                    .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
             assertThat(DeviceConfig.setProperties(properties1)).isTrue();
             assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
                     .isEqualTo(VALUE);
 
             // Test disabled (persistent). Persistence is not actually tested, that would require
             // a host test.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
-            assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
+            assertThat(DeviceConfig.getSyncDisabledMode())
+                    .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
             assertThat(DeviceConfig.setProperties(properties2)).isFalse();
             assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
                     .isEqualTo(VALUE);
 
             // Return to not disabled.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
-            assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+            assertThat(DeviceConfig.getSyncDisabledMode())
+                    .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
             assertThat(DeviceConfig.setProperties(properties2)).isTrue();
             assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
                     .isEqualTo(VALUE2);
 
             // Test disabled (persistent). Absence of persistence is not actually tested, that would
             // require a host test.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
-            assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
+            assertThat(DeviceConfig.getSyncDisabledMode())
+                    .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
             assertThat(DeviceConfig.setProperties(properties1)).isFalse();
             assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
                     .isEqualTo(VALUE2);
 
             // Return to not disabled.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
-            assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+            assertThat(DeviceConfig.getSyncDisabledMode())
+                    .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
             assertThat(DeviceConfig.setProperties(properties1)).isTrue();
             assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
                     .isEqualTo(VALUE);
         } finally {
             // Try to return to the default sync disabled state in case of failure.
-            DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+            DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
 
             // NAMESPACE will be cleared by cleanUp()
         }
@@ -827,4 +832,80 @@
         return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
     }
 
+    @Test
+    public void deleteProperty_nullNamespace() {
+        try {
+            DeviceConfig.deleteProperty(null, KEY);
+            Assert.fail("Null namespace should have resulted in an NPE.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void deleteProperty_nullName() {
+        try {
+            DeviceConfig.deleteProperty(NAMESPACE, null);
+            Assert.fail("Null name should have resulted in an NPE.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void deletePropertyString() {
+        final String value = "new_value";
+        final String default_value = "default";
+        DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyBoolean() {
+        final boolean value = true;
+        final boolean default_value = false;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyInt() {
+        final int value = 123;
+        final int default_value = 999;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyLong() {
+        final long value = 456789;
+        final long default_value = 123456;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deletePropertyFloat() {
+        final float value = 456.789f;
+        final float default_value = 123.456f;
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+        DeviceConfig.deleteProperty(NAMESPACE, KEY);
+        final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+        assertThat(result).isEqualTo(default_value);
+    }
+
+    @Test
+    public void deleteProperty_empty() {
+        assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+        assertThat(result).isNull();
+    }
 }
diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java
index 05e8bd8..0a99b08 100644
--- a/core/tests/coretests/src/android/view/WindowInfoTest.java
+++ b/core/tests/coretests/src/android/view/WindowInfoTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
+import android.app.ActivityTaskManager;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -83,6 +84,7 @@
         assertEquals(0, w.layer);
         assertEquals(AccessibilityNodeInfo.UNDEFINED_NODE_ID, w.accessibilityIdOfAnchor);
         assertEquals(Display.INVALID_DISPLAY, w.displayId);
+        assertEquals(ActivityTaskManager.INVALID_TASK_ID, w.taskId);
         assertNull(w.title);
         assertNull(w.token);
         assertNull(w.childTokens);
@@ -123,6 +125,7 @@
         windowInfo.displayId = 2;
         windowInfo.layer = 3;
         windowInfo.accessibilityIdOfAnchor = 4L;
+        windowInfo.taskId = 5;
         windowInfo.title = "title";
         windowInfo.token = mock(IBinder.class);
         windowInfo.childTokens = new ArrayList<>();
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 46c96c9..2c8c385 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,6 +21,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.os.Parcel;
+import android.view.Display;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -41,14 +42,14 @@
     // and assertAccessibilityEventCleared
 
     /** The number of properties of the {@link AccessibilityEvent} class. */
-    private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
+    private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
 
     // The number of fields tested in the corresponding CTS AccessibilityRecordTest:
     // assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
     // and assertEqualAccessibilityRecord
 
     /** The number of properties of the {@link AccessibilityRecord} class. */
-    private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 24;
+    private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
 
     @Test
     public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
@@ -69,6 +70,14 @@
     }
 
     @Test
+    public void testSourceDisplayId_getSetWorkAcrossParceling() {
+        final int sourceDisplayId = Display.DEFAULT_DISPLAY;
+        AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setDisplayId(sourceDisplayId);
+        assertEquals(sourceDisplayId, copyEventViaParcel(event).getDisplayId());
+    }
+
+    @Test
     public void testWindowChanges_getSetWorkAcrossParceling() {
         final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
                 | AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8643a37..3045d7d5 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -34,6 +34,8 @@
 public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub {
     public void setServiceInfo(AccessibilityServiceInfo info) {}
 
+    public void setAttributionTag(String attributionTag) {}
+
     public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
             long accessibilityNodeId, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -166,4 +168,7 @@
 
     public void logTrace(long timestamp, String where, String callingParams, int processId,
             long threadId, int callingUid, Bundle callingStack) {}
+
+    public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+            int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
 }
diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java
new file mode 100644
index 0000000..5b92471
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ProgressBarTest {
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private ProgressBar mBar;
+    private AccessibilityNodeInfo mInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        // enable accessibility
+        mInstrumentation.getUiAutomation();
+        // create ProgressBar on main thread and call setProgress on main thread
+        mInstrumentation.runOnMainSync(() ->
+                mBar = new ProgressBar(
+                        InstrumentationRegistry.getInstrumentation().getContext(),
+                        null,
+                        com.android.internal.R.attr.progressBarStyleHorizontal
+                )
+        );
+        mInfo = AccessibilityNodeInfo.obtain();
+    }
+
+    @After
+    public void tearDown() {
+        mInfo.recycle();
+    }
+
+    @Test
+    public void testStateDescription_determinateProgressBar_default() {
+        mBar.setIndeterminate(false);
+        assertFalse(mBar.isIndeterminate());
+
+        mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("50%", mInfo.getStateDescription().toString());
+    }
+
+    @Test
+    public void testStateDescription_determinateProgressBar_custom_viewApi() {
+        mBar.setIndeterminate(false);
+        assertFalse(mBar.isIndeterminate());
+        // A workaround for the not-attached ProgressBar.
+        mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                info.setStateDescription(host.getStateDescription());
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+        });
+
+        mBar.setStateDescription("custom state");
+        mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+        assertEquals("custom state", mBar.getStateDescription().toString());
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("custom state", mInfo.getStateDescription().toString());
+
+        mBar.setStateDescription(null);
+
+        assertNull(mBar.getStateDescription());
+        mInfo.recycle();
+        mInfo = AccessibilityNodeInfo.obtain();
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("50%", mInfo.getStateDescription().toString());
+    }
+
+    @Test
+    public void testStateDescription_determinateProgressBar_custom_accessibilityNodeInfoApi() {
+        mBar.setIndeterminate(false);
+        assertFalse(mBar.isIndeterminate());
+        mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setStateDescription("custom state");
+            }
+        });
+
+        mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("custom state", mInfo.getStateDescription().toString());
+    }
+
+    @Test
+    public void testStateDescription_indeterminateProgressBar_default() {
+        mBar.setIndeterminate(true);
+        assertTrue(mBar.isIndeterminate());
+
+        // call setMax to invoke call to ProgressBar#onProgressRefresh()
+        mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("in progress", mInfo.getStateDescription().toString());
+    }
+
+    @Test
+    public void testStateDescription_indeterminateProgressBar_custom_viewApi() {
+        mBar.setIndeterminate(true);
+        assertTrue(mBar.isIndeterminate());
+        // A workaround for the not-attached ProgressBar.
+        mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                info.setStateDescription(host.getStateDescription());
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+        });
+
+        mBar.setStateDescription("custom state");
+        // call setMax to invoke call to ProgressBar#onProgressRefresh()
+        mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+        assertEquals("custom state", mBar.getStateDescription().toString());
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("custom state", mInfo.getStateDescription().toString());
+
+        mBar.setStateDescription(null);
+
+        assertNull(mBar.getStateDescription());
+        mInfo.recycle();
+        mInfo = AccessibilityNodeInfo.obtain();
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("in progress", mInfo.getStateDescription().toString());
+    }
+
+    @Test
+    public void testStateDescription_indeterminateProgressBar_custom_accessibilityNodeInfoApi() {
+        mBar.setIndeterminate(true);
+        assertTrue(mBar.isIndeterminate());
+        mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setStateDescription("custom state");
+            }
+        });
+
+        // call setMax to invoke call to ProgressBar#onProgressRefresh()
+        mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+        mBar.onInitializeAccessibilityNodeInfo(mInfo);
+        assertEquals("custom state", mInfo.getStateDescription().toString());
+    }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 020f4a0..a6e351d 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -22,12 +22,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 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.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 import android.view.IWindowManager;
@@ -38,6 +41,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 /**
  * Tests for {@link WindowContextController}
@@ -53,15 +58,18 @@
 @Presubmit
 public class WindowContextControllerTest {
     private WindowContextController mController;
+    @Mock
     private IWindowManager mMockWms;
+    @Mock
+    private WindowTokenClient mMockToken;
 
     @Before
     public void setUp() throws Exception {
-        mMockWms = mock(IWindowManager.class);
-        mController = new WindowContextController(new Binder(), mMockWms);
-
-        doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(),
-                anyInt(), any());
+        MockitoAnnotations.initMocks(this);
+        mController = new WindowContextController(mMockToken, mMockWms);
+        doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
+        doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
+                anyInt(), anyInt(), any());
     }
 
     @Test(expected = IllegalStateException.class)
@@ -85,6 +93,8 @@
                 null /* options */);
 
         assertThat(mController.mAttachedToDisplayArea).isTrue();
+        verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
+                eq(false) /* shouldReportConfigChange */);
 
         mController.detachIfNeeded();
 
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 0000000..045b3a2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.text.ParcelableSpan;
+import android.text.SpannableString;
+import android.text.style.LocaleSpan;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Unit tests for AccessibilityUtils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityUtilsTest {
+    @Test
+    public void textOrSpanChanged_stringChange_returnTextChange() {
+        final CharSequence beforeText = "a";
+
+        final CharSequence afterText = "b";
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.TEXT, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_stringNotChange_returnNoneChange() {
+        final CharSequence beforeText = "a";
+
+        final CharSequence afterText = "a";
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.NONE, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_nonSpanToNonParcelableSpan_returnNoneChange() {
+        final Object nonParcelableSpan = new Object();
+        final CharSequence beforeText = new SpannableString("a");
+
+        final SpannableString afterText = new SpannableString("a");
+        afterText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.NONE, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_nonSpanToParcelableSpan_returnParcelableSpanChange() {
+        final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+        final CharSequence beforeText = new SpannableString("a");
+
+        final SpannableString afterText = new SpannableString("a");
+        afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_nonParcelableSpanToParcelableSpan_returnParcelableSpanChange() {
+        final Object nonParcelableSpan = new Object();
+        final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+        final SpannableString beforeText = new SpannableString("a");
+        beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+        SpannableString afterText = new SpannableString("a");
+        afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_nonParcelableSpanChange_returnNoneChange() {
+        final Object nonParcelableSpan = new Object();
+        final SpannableString beforeText = new SpannableString("a");
+        beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+        final SpannableString afterText = new SpannableString("a");
+        afterText.setSpan(nonParcelableSpan, 1, 1, 0);
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.NONE, type);
+    }
+
+    @Test
+    public void textOrSpanChanged_parcelableSpanChange_returnParcelableSpanChange() {
+        final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+        final SpannableString beforeText = new SpannableString("a");
+        beforeText.setSpan(parcelableSpan, 0, 1, 0);
+
+        final SpannableString afterText = new SpannableString("a");
+        afterText.setSpan(parcelableSpan, 1, 1, 0);
+
+        @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+                beforeText, afterText);
+        assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 54d8701..65df390 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -1263,21 +1263,21 @@
                 mBatteryStatsImpl.new UidToRemove(1, mClocks.elapsedRealtime()));
         mBatteryStatsImpl.getPendingRemovedUids().add(
                 mBatteryStatsImpl.new UidToRemove(5, 10, mClocks.elapsedRealtime()));
-        mBatteryStatsImpl.clearPendingRemovedUids();
+        mBatteryStatsImpl.clearPendingRemovedUidsLocked();
         assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
 
         mClocks.realtime = mClocks.uptime = 100_000;
-        mBatteryStatsImpl.clearPendingRemovedUids();
+        mBatteryStatsImpl.clearPendingRemovedUidsLocked();
         assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
 
         mClocks.realtime = mClocks.uptime = 200_000;
         mBatteryStatsImpl.getPendingRemovedUids().add(
                 mBatteryStatsImpl.new UidToRemove(100, mClocks.elapsedRealtime()));
-        mBatteryStatsImpl.clearPendingRemovedUids();
+        mBatteryStatsImpl.clearPendingRemovedUidsLocked();
         assertEquals(3, mBatteryStatsImpl.getPendingRemovedUids().size());
 
         mClocks.realtime = mClocks.uptime = 400_000;
-        mBatteryStatsImpl.clearPendingRemovedUids();
+        mBatteryStatsImpl.clearPendingRemovedUidsLocked();
         assertEquals(1, mBatteryStatsImpl.getPendingRemovedUids().size());
         verify(mCpuUidActiveTimeReader).removeUid(1);
         verify(mCpuUidActiveTimeReader).removeUidsInRange(5, 10);
@@ -1289,7 +1289,7 @@
         verify(mCpuUidUserSysTimeReader).removeUidsInRange(5, 10);
 
         mClocks.realtime = mClocks.uptime = 800_000;
-        mBatteryStatsImpl.clearPendingRemovedUids();
+        mBatteryStatsImpl.clearPendingRemovedUidsLocked();
         assertEquals(0, mBatteryStatsImpl.getPendingRemovedUids().size());
         verify(mCpuUidActiveTimeReader).removeUid(100);
         verify(mCpuUidClusterTimeReader).removeUid(100);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index dd814e6..53845a7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -56,7 +56,6 @@
     }
 
     @SmallTest
-    @SkipPresubmit("b/180015146")
     public void testEndSampleAndContinueWhenTimeOrCountDecreases() throws Exception {
         final MockClocks clocks = new MockClocks();
         final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
@@ -72,7 +71,10 @@
         assertEquals(0, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
 
         // This is less than we currently have, so we will end the sample. Time isn't running, so
-        // nothing should happen.
+        // nothing should happen, except that tracking will stop.
+        timer.update(0, 0, SystemClock.elapsedRealtime() * 1000);
+
+        // Start tracking again
         timer.update(0, 0, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -86,9 +88,13 @@
         assertEquals(100, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(10, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
 
-        // This is less than we currently have, so we should end our sample and continue with the
-        // entire amount updated here.
-        timer.update(50, 5, SystemClock.elapsedRealtime() * 1000);
+        // This is less than we currently have, so we should end our sample.
+        timer.update(30, 3, SystemClock.elapsedRealtime() * 1000);
+
+        // Restart tracking
+        timer.update(30, 3, SystemClock.elapsedRealtime() * 1000);
+
+        timer.add(50, 5);
 
         assertEquals(150, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(15, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
index e90bcb7..de50dcb 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
@@ -51,6 +51,7 @@
     private static final long POLL_INTERVAL_MS = 500;
     private static final long USER_REMOVE_TIMEOUT_MS = 5_000;
     private static final long STOP_USER_TIMEOUT_MS = 10_000;
+    private static final long USER_UIDS_REMOVE_TIMEOUT_MS = 15_000;
     private static final long BATTERYSTATS_POLLING_TIMEOUT_MS = 5_000;
 
     private static final String CPU_DATA_TAG = "cpu";
@@ -78,7 +79,6 @@
     }
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testNoCpuDataForRemovedUser() throws Exception {
         mIam.startUserInBackground(mTestUserId);
         waitUntilTrue("No uids for started user " + mTestUserId,
@@ -108,7 +108,7 @@
             return true;
         }, USER_REMOVE_TIMEOUT_MS);
         waitUntilTrue("Uids still found for removed user " + mTestUserId,
-                () -> getNumberOfUidsInBatteryStats() == 0, BATTERYSTATS_POLLING_TIMEOUT_MS);
+                () -> getNumberOfUidsInBatteryStats() == 0, USER_UIDS_REMOVE_TIMEOUT_MS);
     }
 
     @After
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 99d576d..d9b69b1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -190,6 +190,11 @@
         }
 
         @Override
+        public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
+            return null;
+        }
+
+        @Override
         public Future<?> scheduleCpuSyncDueToRemovedUid(int uid) {
             return null;
         }
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 272f228..7d4412c 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.view.InsetsState;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -59,7 +60,8 @@
                 new Binder() /* imeToken */,
                 true /* navbarColorManagedByIme */,
                 BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
-                true /* appFullscreen */,
+                new InsetsState() /* requestedState */,
+                "test" /* packageName */,
                 new int[0] /* transientBarTypes */);
 
         final RegisterStatusBarResult copy = clone(original);
@@ -79,7 +81,8 @@
         assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
         assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
         assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
-        assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+        assertThat(copy.mRequestedState).isEqualTo(original.mRequestedState);
+        assertThat(copy.mPackageName).isEqualTo(original.mPackageName);
         assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
new file mode 100644
index 0000000..fdba811
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+import static com.android.internal.util.BitUtils.bytesToBEInt;
+import static com.android.internal.util.BitUtils.bytesToLEInt;
+import static com.android.internal.util.BitUtils.getUint16;
+import static com.android.internal.util.BitUtils.getUint32;
+import static com.android.internal.util.BitUtils.getUint8;
+import static com.android.internal.util.BitUtils.packBits;
+import static com.android.internal.util.BitUtils.uint16;
+import static com.android.internal.util.BitUtils.uint32;
+import static com.android.internal.util.BitUtils.uint8;
+import static com.android.internal.util.BitUtils.unpackBits;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitUtilsTest {
+
+    @Test
+    public void testUnsignedByteWideningConversions() {
+        byte b0 = 0;
+        byte b1 = 1;
+        byte bm1 = -1;
+        assertEquals(0, uint8(b0));
+        assertEquals(1, uint8(b1));
+        assertEquals(127, uint8(Byte.MAX_VALUE));
+        assertEquals(128, uint8(Byte.MIN_VALUE));
+        assertEquals(255, uint8(bm1));
+        assertEquals(255, uint8((byte)255));
+    }
+
+    @Test
+    public void testUnsignedShortWideningConversions() {
+        short s0 = 0;
+        short s1 = 1;
+        short sm1 = -1;
+        assertEquals(0, uint16(s0));
+        assertEquals(1, uint16(s1));
+        assertEquals(32767, uint16(Short.MAX_VALUE));
+        assertEquals(32768, uint16(Short.MIN_VALUE));
+        assertEquals(65535, uint16(sm1));
+        assertEquals(65535, uint16((short)65535));
+    }
+
+    @Test
+    public void testUnsignedShortComposition() {
+        byte b0 = 0;
+        byte b1 = 1;
+        byte b2 = 2;
+        byte b10 = 10;
+        byte b16 = 16;
+        byte b128 = -128;
+        byte b224 = -32;
+        byte b255 = -1;
+        assertEquals(0x0000, uint16(b0, b0));
+        assertEquals(0xffff, uint16(b255, b255));
+        assertEquals(0x0a01, uint16(b10, b1));
+        assertEquals(0x8002, uint16(b128, b2));
+        assertEquals(0x01ff, uint16(b1, b255));
+        assertEquals(0x80ff, uint16(b128, b255));
+        assertEquals(0xe010, uint16(b224, b16));
+    }
+
+    @Test
+    public void testUnsignedIntWideningConversions() {
+        assertEquals(0, uint32(0));
+        assertEquals(1, uint32(1));
+        assertEquals(2147483647L, uint32(Integer.MAX_VALUE));
+        assertEquals(2147483648L, uint32(Integer.MIN_VALUE));
+        assertEquals(4294967295L, uint32(-1));
+        assertEquals(4294967295L, uint32((int)4294967295L));
+    }
+
+    @Test
+    public void testBytesToInt() {
+        assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0)));
+        assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255)));
+        assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1)));
+        assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2)));
+        assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255)));
+        assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1)));
+
+        assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0)));
+        assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1)));
+        assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255)));
+    }
+
+    @Test
+    public void testUnsignedGetters() {
+        ByteBuffer b = ByteBuffer.allocate(4);
+        b.putInt(0xffff);
+
+        assertEquals(0x0, getUint8(b, 0));
+        assertEquals(0x0, getUint8(b, 1));
+        assertEquals(0xff, getUint8(b, 2));
+        assertEquals(0xff, getUint8(b, 3));
+
+        assertEquals(0x0, getUint16(b, 0));
+        assertEquals(0xffff, getUint16(b, 2));
+
+        b.rewind();
+        b.putInt(0xffffffff);
+        assertEquals(0xffffffffL, getUint32(b, 0));
+    }
+
+    @Test
+    public void testBitsPacking() {
+        BitPackingTestCase[] testCases = {
+            new BitPackingTestCase(0, ints()),
+            new BitPackingTestCase(1, ints(0)),
+            new BitPackingTestCase(2, ints(1)),
+            new BitPackingTestCase(3, ints(0, 1)),
+            new BitPackingTestCase(4, ints(2)),
+            new BitPackingTestCase(6, ints(1, 2)),
+            new BitPackingTestCase(9, ints(0, 3)),
+            new BitPackingTestCase(~Long.MAX_VALUE, ints(63)),
+            new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)),
+            new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)),
+        };
+        for (BitPackingTestCase tc : testCases) {
+            int[] got = unpackBits(tc.packedBits);
+            assertTrue(
+                    "unpackBits("
+                            + tc.packedBits
+                            + "): expected "
+                            + Arrays.toString(tc.bits)
+                            + " but got "
+                            + Arrays.toString(got),
+                    Arrays.equals(tc.bits, got));
+        }
+        for (BitPackingTestCase tc : testCases) {
+            long got = packBits(tc.bits);
+            assertEquals(
+                    "packBits("
+                            + Arrays.toString(tc.bits)
+                            + "): expected "
+                            + tc.packedBits
+                            + " but got "
+                            + got,
+                    tc.packedBits,
+                    got);
+        }
+
+        long[] moreTestCases = {
+            0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(),
+        };
+        for (long l : moreTestCases) {
+            assertEquals(l, packBits(unpackBits(l)));
+        }
+    }
+
+    static byte[] bytes(int b1, int b2, int b3, int b4) {
+        return new byte[] {b(b1), b(b2), b(b3), b(b4)};
+    }
+
+    static byte b(int i) {
+        return (byte) i;
+    }
+
+    static int[] ints(int... array) {
+        return array;
+    }
+
+    static class BitPackingTestCase {
+        final int[] bits;
+        final long packedBits;
+
+        BitPackingTestCase(long packedBits, int[] bits) {
+            this.bits = bits;
+            this.packedBits = packedBits;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
new file mode 100644
index 0000000..4497770
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RingBufferTest {
+
+    @Test
+    public void testEmptyRingBuffer() {
+        RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+        assertArrayEquals(new String[0], buffer.toArray());
+    }
+
+    @Test
+    public void testIncorrectConstructorArguments() {
+        try {
+            RingBuffer<String> buffer = new RingBuffer<>(String.class, -10);
+            fail("Should not be able to create a negative capacity RingBuffer");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            RingBuffer<String> buffer = new RingBuffer<>(String.class, 0);
+            fail("Should not be able to create a 0 capacity RingBuffer");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testRingBufferWithNoWrapping() {
+        RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+        buffer.append("a");
+        buffer.append("b");
+        buffer.append("c");
+        buffer.append("d");
+        buffer.append("e");
+
+        String[] expected = {"a", "b", "c", "d", "e"};
+        assertArrayEquals(expected, buffer.toArray());
+    }
+
+    @Test
+    public void testRingBufferWithCapacity1() {
+        RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
+
+        buffer.append("a");
+        assertArrayEquals(new String[]{"a"}, buffer.toArray());
+
+        buffer.append("b");
+        assertArrayEquals(new String[]{"b"}, buffer.toArray());
+
+        buffer.append("c");
+        assertArrayEquals(new String[]{"c"}, buffer.toArray());
+
+        buffer.append("d");
+        assertArrayEquals(new String[]{"d"}, buffer.toArray());
+
+        buffer.append("e");
+        assertArrayEquals(new String[]{"e"}, buffer.toArray());
+    }
+
+    @Test
+    public void testRingBufferWithWrapping() {
+        int capacity = 100;
+        RingBuffer<String> buffer = new RingBuffer<>(String.class, capacity);
+
+        buffer.append("a");
+        buffer.append("b");
+        buffer.append("c");
+        buffer.append("d");
+        buffer.append("e");
+
+        String[] expected1 = {"a", "b", "c", "d", "e"};
+        assertArrayEquals(expected1, buffer.toArray());
+
+        String[] expected2 = new String[capacity];
+        int firstIndex = 0;
+        int lastIndex = capacity - 1;
+
+        expected2[firstIndex] = "e";
+        for (int i = 1; i < capacity; i++) {
+            buffer.append("x");
+            expected2[i] = "x";
+        }
+        assertArrayEquals(expected2, buffer.toArray());
+
+        buffer.append("x");
+        expected2[firstIndex] = "x";
+        assertArrayEquals(expected2, buffer.toArray());
+
+        for (int i = 0; i < 10; i++) {
+            for (String s : expected2) {
+                buffer.append(s);
+            }
+        }
+        assertArrayEquals(expected2, buffer.toArray());
+
+        buffer.append("a");
+        expected2[lastIndex] = "a";
+        assertArrayEquals(expected2, buffer.toArray());
+    }
+
+    @Test
+    public void testGetNextSlot() {
+        int capacity = 100;
+        RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+        final DummyClass1[] actual = new DummyClass1[capacity];
+        final DummyClass1[] expected = new DummyClass1[capacity];
+        for (int i = 0; i < capacity; ++i) {
+            final DummyClass1 obj = buffer.getNextSlot();
+            obj.x = capacity * i;
+            actual[i] = obj;
+            expected[i] = new DummyClass1();
+            expected[i].x = capacity * i;
+        }
+        assertArrayEquals(expected, buffer.toArray());
+
+        for (int i = 0; i < capacity; ++i) {
+            if (actual[i] != buffer.getNextSlot()) {
+                fail("getNextSlot() should re-use objects if available");
+            }
+        }
+
+        RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(No nullary constructor)", buffer2.getNextSlot());
+
+        RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(Inaccessible class)", buffer3.getNextSlot());
+    }
+
+    public static final class DummyClass1 {
+        int x;
+
+        public boolean equals(Object o) {
+            if (o instanceof DummyClass1) {
+                final DummyClass1 other = (DummyClass1) o;
+                return other.x == this.x;
+            }
+            return false;
+        }
+    }
+
+    public static final class DummyClass2 {
+        public DummyClass2(int x) {}
+    }
+
+    private static final class DummyClass3 {}
+}
diff --git a/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
new file mode 100644
index 0000000..996d7b4
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+
+import android.app.ResourcesManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link ConfigurationHelper}
+ *
+ * <p>Build/Install/Run:
+ *  atest FrameworksMockingCoreTests:ConfigurationHelperTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ConfigurationHelperTest {
+    MockitoSession mMockitoSession;
+    ResourcesManager mResourcesManager;
+
+    @Before
+    public void setUp() {
+        mMockitoSession = mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(ResourcesManager.class)
+                .startMocking();
+        doReturn(mock(ResourcesManager.class)).when(ResourcesManager::getInstance);
+        mResourcesManager = ResourcesManager.getInstance();
+    }
+
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
+    }
+
+    @Test
+    public void testShouldUpdateResources_NullConfig_ReturnsTrue() {
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), null /* config */,
+                new Configuration(), new Configuration(), false /* displayChanged */,
+                null /* configChanged */)).isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_DisplayChanged_ReturnsTrue() {
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+                new Configuration(), new Configuration(), true /* displayChanged */,
+                null /* configChanged */)).isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_DifferentResources_ReturnsTrue() {
+        doReturn(false).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+                new Configuration(), new Configuration(), false /* displayChanged */,
+                null /* configChanged */)).isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_DifferentBounds_ReturnsTrue() {
+        doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        final Configuration config = new Configuration();
+        config.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+        config.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+        final Configuration newConfig = new Configuration();
+        newConfig.windowConfiguration.setBounds(new Rect(0, 0, 20, 20));
+        newConfig.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+                new Configuration(), false /* displayChanged */, null /* configChanged */))
+                .isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_SameConfig_ReturnsFalse() {
+        doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        final Configuration config = new Configuration();
+        final Configuration newConfig = new Configuration();
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+                new Configuration(), false /* displayChanged */, null /* configChanged */))
+                .isFalse();
+    }
+
+    @Test
+    public void testShouldUpdateResources_DifferentConfig_ReturnsTrue() {
+        doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        final Configuration config = new Configuration();
+        final Configuration newConfig = new Configuration();
+        newConfig.setToDefaults();
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+                new Configuration(), false /* displayChanged */, null /* configChanged */))
+                .isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_DifferentNonPublicConfig_ReturnsTrue() {
+        doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        final Configuration config = new Configuration();
+        final Configuration newConfig = new Configuration();
+        newConfig.windowConfiguration.setAppBounds(new Rect(0, 0, 10, 10));
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+                new Configuration(), false /* displayChanged */, null /* configChanged */))
+                .isTrue();
+    }
+
+    @Test
+    public void testShouldUpdateResources_OverrideConfigChanged_ReturnsFalse() {
+        doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+        final Configuration config = new Configuration();
+        final Configuration newConfig = new Configuration();
+        final boolean configChanged = true;
+
+        assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+                new Configuration(), false /* displayChanged */, configChanged))
+                .isEqualTo(configChanged);
+    }
+}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 084e1db..d1e4322 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -130,6 +130,13 @@
 }
 
 prebuilt_etc {
+    name: "allowed_privapp_com.google.android.car.adaslocation",
+    sub_dir: "permissions",
+    src: "com.google.android.car.adaslocation.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "allowed_privapp_com.google.android.car.kitchensink",
     sub_dir: "permissions",
     src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.android.car.activityresolver.xml b/data/etc/car/com.android.car.activityresolver.xml
index d48bc15..927c738 100644
--- a/data/etc/car/com.android.car.activityresolver.xml
+++ b/data/etc/car/com.android.car.activityresolver.xml
@@ -19,3 +19,4 @@
         <permission name="android.permission.MANAGE_USERS"/>
     </privapp-permissions>
 </permissions>
+
diff --git a/data/etc/car/com.google.android.car.adaslocation.xml b/data/etc/car/com.google.android.car.adaslocation.xml
new file mode 100644
index 0000000..cc1ef3c
--- /dev/null
+++ b/data/etc/car/com.google.android.car.adaslocation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.google.android.car.adaslocation">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 813b799..07389b2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -187,7 +187,6 @@
         <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
         <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
         <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
-        <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SHUTDOWN"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
         <permission name="android.permission.STATUS_BAR"/>
@@ -202,6 +201,8 @@
         <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+        <permission name="android.permission.LOG_COMPAT_CHANGE"/>
+        <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.calendar">
@@ -342,6 +343,8 @@
         <!-- Needed for test only -->
         <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
         <permission name="android.permission.POWER_SAVER" />
+        <!-- Needed for CTS tests -->
+        <permission name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"/>
         <permission name="android.permission.READ_CARRIER_APP_INFO"/>
         <permission name="android.permission.READ_FRAME_BUFFER"/>
         <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -533,6 +536,11 @@
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.wallpaper.livepicker">
+        <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
+        <permission name="android.permission.BIND_WALLPAPER"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.dynsystem">
         <permission name="android.permission.REBOOT"/>
         <permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b67988e..8caff23 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "-2029985709": {
-      "message": "setFocusedTask: taskId=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "-2024464438": {
       "message": "app-onAnimationFinished(): mOuter=%s",
       "level": "DEBUG",
@@ -103,6 +97,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-2010331310": {
+      "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-2006946193": {
       "message": "setClientVisible: %s clientVisible=%b Callers=%s",
       "level": "VERBOSE",
@@ -181,6 +181,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1924376693": {
+      "message": " Setting Ready-group to %b. group=%s from %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-1918702467": {
       "message": "onSyncFinishedDrawing %s",
       "level": "VERBOSE",
@@ -211,6 +217,12 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
     },
+    "-1886145147": {
+      "message": "resumeTopActivity: Going to sleep and all paused",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1884933373": {
       "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
       "level": "INFO",
@@ -247,12 +259,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransition.java"
     },
-    "-1861864501": {
-      "message": "resumeTopActivityLocked: Going to sleep and all paused",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-1844540996": {
       "message": "  Initial targets: %s",
       "level": "VERBOSE",
@@ -325,12 +331,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
-    "-1768090656": {
-      "message": "Re-launching after pause: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-1750384749": {
       "message": "Launch on display check: allow launch on public display",
       "level": "DEBUG",
@@ -415,12 +415,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1655805455": {
-      "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-1647332198": {
       "message": "remove RecentTask %s when finishing user %d",
       "level": "INFO",
@@ -433,6 +427,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
     },
+    "-1633115609": {
+      "message": "Key dispatch not paused for screen off",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1632122349": {
       "message": "Changing surface while display frozen: %s",
       "level": "VERBOSE",
@@ -487,6 +487,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-1564228464": {
+      "message": "App died while pausing: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1559645910": {
       "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s",
       "level": "DEBUG",
@@ -565,12 +571,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityStarter.java"
     },
-    "-1492696222": {
-      "message": "App died during pause, not stopping: %s",
-      "level": "VERBOSE",
-      "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",
@@ -613,6 +613,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1442613680": {
+      "message": " Creating Ready-group for Transition %d with root=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-1438175584": {
       "message": "Input focus has changed to %s display=%d",
       "level": "VERBOSE",
@@ -637,12 +643,24 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1421296808": {
+      "message": "Moving to RESUMED: %s (in existing)",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1419762046": {
       "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
       "level": "DEBUG",
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
+    "-1419461256": {
+      "message": "resumeTopActivity: Resumed %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1413901262": {
       "message": "startRecentsActivity(): intent=%s",
       "level": "DEBUG",
@@ -709,6 +727,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1311436264": {
+      "message": "Unregister task fragment organizer=%s uid=%d pid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "-1305966693": {
       "message": "Sending position change to %s, onTop: %b",
       "level": "VERBOSE",
@@ -805,6 +829,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1187377055": {
+      "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-1176488860": {
       "message": "SURFACE isSecure=%b: %s",
       "level": "INFO",
@@ -919,12 +949,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
-    "-1066383762": {
-      "message": "Sleep still waiting to pause %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-1060365734": {
       "message": "Attempted to add QS dialog window with bad token %s.  Aborting.",
       "level": "WARN",
@@ -985,6 +1009,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-957060823": {
+      "message": "Moving to PAUSING: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-951939129": {
       "message": "Unregister task organizer=%s uid=%d",
       "level": "VERBOSE",
@@ -1201,6 +1231,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-706481945": {
+      "message": "TaskFragment parent info changed name=%s parentTaskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "-705939410": {
       "message": "Waiting for pause to complete...",
       "level": "VERBOSE",
@@ -1237,12 +1273,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "-672228342": {
-      "message": "resumeTopActivityLocked: Top activity resumed %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-668956537": {
       "message": "  THUMBNAIL %s: CREATE",
       "level": "INFO",
@@ -1267,11 +1297,11 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "-650261962": {
-      "message": "Sleep needs to pause %s",
+    "-648891906": {
+      "message": "Activity not running or entered PiP, resuming next.",
       "level": "VERBOSE",
       "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
     "-641258376": {
       "message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...",
@@ -1309,12 +1339,6 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-606328116": {
-      "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-597091183": {
       "message": "Delete TaskDisplayArea uid=%d",
       "level": "VERBOSE",
@@ -1375,11 +1399,11 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowAnimator.java"
     },
-    "-533690126": {
-      "message": "resumeTopActivityLocked: Resumed %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
+    "-542756093": {
+      "message": "TaskFragment vanished name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
     },
     "-532081937": {
       "message": "  Commit activity becoming invisible: %s",
@@ -1387,11 +1411,11 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "-527683022": {
-      "message": "resumeTopActivityLocked: Skip resume: some activity pausing.",
+    "-521613870": {
+      "message": "App died during pause, not stopping: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
     "-519504830": {
       "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s",
@@ -1477,18 +1501,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
-    "-427457280": {
-      "message": "App died while pausing: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
-    "-417514857": {
-      "message": "Key dispatch not paused for screen off",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-415865166": {
       "message": "findFocusedWindow: Found new focus @ %s",
       "level": "VERBOSE",
@@ -1597,11 +1609,17 @@
       "group": "WM_DEBUG_LOCKTASK",
       "at": "com\/android\/server\/wm\/LockTaskController.java"
     },
-    "-303497363": {
-      "message": "reparent: moving activity=%s to task=%d at %d",
+    "-312353598": {
+      "message": "Executing finish of activity: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
+    "-310337305": {
+      "message": "Activity config changed during resume: %s, new next: %s",
       "level": "INFO",
-      "group": "WM_DEBUG_ADD_REMOVE",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
     "-302468788": {
       "message": "Expected target rootTask=%s to be top most but found rootTask=%s",
@@ -1621,12 +1639,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-279436615": {
-      "message": "Moving to PAUSING: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-262984451": {
       "message": "Relaunch failed %s",
       "level": "INFO",
@@ -1639,6 +1651,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-248761393": {
+      "message": "startPausing: taskFrag =%s mResumedActivity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-240296576": {
       "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
       "level": "VERBOSE",
@@ -1651,12 +1669,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-234244777": {
-      "message": "Activity config changed during resume: %s, new next: %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-230587670": {
       "message": "SyncGroup %d:  Unfinished container: %s",
       "level": "VERBOSE",
@@ -1723,12 +1735,6 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-118786523": {
-      "message": "Resume failed; resetting state to %s: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-116086365": {
       "message": "******************** ENABLING SCREEN!",
       "level": "INFO",
@@ -1777,6 +1783,18 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/Session.java"
     },
+    "-80004683": {
+      "message": "Resume failed; resetting state to %s: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
+    "-55185509": {
+      "message": "setFocusedTask: taskId=%d touchedActivity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-50336993": {
       "message": "moveFocusableActivityToTop: activity=%s",
       "level": "DEBUG",
@@ -1909,12 +1927,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
     },
-    "94402792": {
-      "message": "Moving to RESUMED: %s (in existing)",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "95216706": {
       "message": "hideIme target: %s ",
       "level": "DEBUG",
@@ -1933,6 +1945,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
+    "102618780": {
+      "message": "resumeTopActivity: Pausing %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "108170907": {
       "message": "Add starting %s: startingData=%s",
       "level": "VERBOSE",
@@ -2173,6 +2191,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "327461496": {
+      "message": "Complete pause: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
+    "341055768": {
+      "message": "resumeTopActivity: Skip resume: need to start pausing",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "342460966": {
       "message": "DRAG %s: pos=(%d,%d)",
       "level": "INFO",
@@ -2191,6 +2221,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "352982444": {
+      "message": " allReady query: used=%b override=%b states=[%s]",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "355720268": {
       "message": "stopFreezingDisplayLocked: Unfreezing now",
       "level": "DEBUG",
@@ -2227,11 +2263,11 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "391189028": {
-      "message": "pauseBackTasks: task=%s mResumedActivity=%s",
-      "level": "DEBUG",
+    "378825104": {
+      "message": "Enqueueing pending pause: %s",
+      "level": "VERBOSE",
       "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
     "397105698": {
       "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
@@ -2365,6 +2401,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
     },
+    "573582981": {
+      "message": "reparent: moving activity=%s to new task fragment in task=%d at %d",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "579298675": {
       "message": "Moving to DESTROYED: %s (removed from history)",
       "level": "VERBOSE",
@@ -2467,6 +2509,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "660908897": {
+      "message": "Auto-PIP allowed, entering PIP mode directly: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "662572728": {
       "message": "Attempted to add a toast window with bad token %s.  Aborting.",
       "level": "WARN",
@@ -2485,12 +2533,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "669361121": {
+      "message": "Sleep still need to stop %d activities",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "674932310": {
       "message": "Setting Intent of %s to target %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "675705156": {
+      "message": "resumeTopActivity: Top activity resumed %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "685047360": {
       "message": "Resizing window %s",
       "level": "VERBOSE",
@@ -2521,12 +2581,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
     },
-    "709500946": {
-      "message": "resumeTopActivityLocked: Skip resume: need to start pausing",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "715749922": {
       "message": "Allowlisting %d:%s",
       "level": "WARN",
@@ -2629,12 +2683,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
-    "897964776": {
-      "message": "Complete pause: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "898863925": {
       "message": "Attempted to add QS dialog window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -2659,6 +2707,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "935418348": {
+      "message": "resumeTopActivity: Skip resume: some activity pausing.",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "950074526": {
       "message": "setLockTaskMode: Can't lock due to auth",
       "level": "WARN",
@@ -2707,11 +2761,11 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
     },
-    "988389910": {
-      "message": "resumeTopActivityLocked: Pausing %s",
-      "level": "DEBUG",
+    "987903142": {
+      "message": "Sleep needs to pause %s",
+      "level": "VERBOSE",
       "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
     "996960396": {
       "message": "Starting Transition %d",
@@ -2719,18 +2773,24 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "1001509841": {
-      "message": "Auto-PIP allowed, entering PIP mode directly: %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "1001904964": {
       "message": "***** BOOT TIMEOUT: forcing display enabled",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1011462000": {
+      "message": "Re-launching after pause: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
+    "1022095595": {
+      "message": "TaskFragment info changed name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "1023413388": {
       "message": "Finish waiting for pause of: %s",
       "level": "VERBOSE",
@@ -2917,6 +2977,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1284122013": {
+      "message": "TaskFragment appeared name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "1288731814": {
       "message": "WindowState.hideLw: setting mFocusMayChange true",
       "level": "INFO",
@@ -3073,6 +3139,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "1494644409": {
+      "message": "  Rejecting as detached: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "1495525537": {
       "message": "createWallpaperAnimations()",
       "level": "DEBUG",
@@ -3163,12 +3235,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
-    "1585450696": {
-      "message": "resumeTopActivityLocked: Restarting %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "1589610525": {
       "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
       "level": "VERBOSE",
@@ -3217,6 +3283,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "1653025361": {
+      "message": "Register task fragment organizer=%s uid=%d pid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "1653210583": {
       "message": "Removing app %s delayed=%b animation=%s animating=%b",
       "level": "VERBOSE",
@@ -3229,6 +3301,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/InsetsStateController.java"
     },
+    "1670933628": {
+      "message": " Setting allReady override",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "1671994402": {
       "message": "Nulling last startingData",
       "level": "VERBOSE",
@@ -3385,18 +3463,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "1837992242": {
-      "message": "Executing finish of activity: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
-    "1847414670": {
-      "message": "Activity not running or entered PiP, resuming next.",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "1853793312": {
       "message": "Notify removed startingWindow %s",
       "level": "VERBOSE",
@@ -3409,6 +3475,12 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1856783490": {
+      "message": "resumeTopActivity: Restarting %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "1865125884": {
       "message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b",
       "level": "DEBUG",
@@ -3421,30 +3493,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "1884961873": {
-      "message": "Sleep still need to stop %d activities",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "1891501279": {
       "message": "cancelAnimation(): reason=%s",
       "level": "DEBUG",
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "1894239744": {
-      "message": "Enqueueing pending pause: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STATES",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "1903353011": {
       "message": "notifyAppStopped: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1912291550": {
+      "message": "Sleep still waiting to pause %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_STATES",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "1918448345": {
       "message": "Task appeared taskId=%d",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl
new file mode 100644
index 0000000..05a25f0
--- /dev/null
+++ b/data/keyboards/Vendor_0171_Product_0419.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Amazon Luna Controller
+#
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Action button (circle icon, left of the Home button)
+key 158   BUTTON_SELECT
+
+# Home button (branded button in the center of the controller)
+key 172   BUTTON_MODE
+
+# Menu button (hamburger icon, right of the Home button)
+key 315   BUTTON_START
+
+# Alexa Push-To-Talk button (microphone icon, below the Home button)
+key 217   MEDIA_RECORD
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
new file mode 100644
index 0000000..3d7b94f
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+
+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;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkUnattributedNoteOpCall",
+    summary = "Verifies that a noteOp() call is attributed",
+    severity = WARNING)
+public final class UnattributedNoteOpCallChecker extends BugChecker
+        implements MethodInvocationTreeMatcher {
+
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteOp(int,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteOp(java.lang.String,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_3 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteOp(int)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteOpNoThrow(int,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteOpNoThrow(java.lang.String,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOp(java.lang.String,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOp(int,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_3 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOp(int)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_4 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOp(int,int,java.lang.String,boolean)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOpNoThrow(java.lang.String,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOpNoThrow(int,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_3 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("startOpNoThrow(int,int,java.lang.String,boolean)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteProxyOp(java.lang.String,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteProxyOp(int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String,int)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_1 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("finishOp(int)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_2 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("finishOp(java.lang.String,int,java.lang.String)"));
+    private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_3 = methodInvocation(
+            instanceMethod().onExactClass("android.app.AppOpsManager")
+                    .withSignature("finishOp(int,int,java.lang.String)"));
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (UNATTRIBUTED_NOTEOP_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_NOTEOP_CALL_2.matches(tree, state)
+            || UNATTRIBUTED_NOTEOP_CALL_3.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed noteOp call! Please use noteOp(int, String, String, String) or noteOp(int, CallerIdentity)")
+                .build();
+        }
+        if (UNATTRIBUTED_NOTEOPNOTHROW_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_NOTEOPNOTHROW_CALL_2.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed noteOpNoThrow call! Please use noteOpNoThrow(String, int, String, String, String)")
+                .build();
+        }
+        if (UNATTRIBUTED_STARTOP_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_STARTOP_CALL_2.matches(tree, state)
+            || UNATTRIBUTED_STARTOP_CALL_3.matches(tree, state)
+            || UNATTRIBUTED_STARTOP_CALL_4.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed startOp call! Please use startOp(int, int, String, boolean, String, String)")
+                .build();
+        }
+        if (UNATTRIBUTED_STARTOPNOTHROW_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_STARTOPNOTHROW_CALL_2.matches(tree, state)
+            || UNATTRIBUTED_STARTOPNOTHROW_CALL_3.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed startOpNoThrow call!")
+                .build();
+        }
+        if (UNATTRIBUTED_NOTEPROXYOP_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_NOTEPROXYOP_CALL_2.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed noteProxyOp call!")
+                .build();
+        }
+        if (UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed noteProxyOpNoThrow call!")
+                .build();
+        }
+        if (UNATTRIBUTED_FINISHOP_CALL_1.matches(tree, state)
+            || UNATTRIBUTED_FINISHOP_CALL_2.matches(tree, state)
+            || UNATTRIBUTED_FINISHOP_CALL_3.matches(tree, state)) {
+            return buildDescription(tree)
+                .setMessage("Unattributed finishOp call!")
+                .build();
+        }
+
+
+
+        return Description.NO_MATCH;
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
new file mode 100644
index 0000000..9a98c7c
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 UnattributedNoteOpCallCheckerTest {
+    private CompilationTestHelper mCompilationHelper;
+
+    @Before
+    public void setUp() {
+        mCompilationHelper = CompilationTestHelper.newInstance(
+                UnattributedNoteOpCallChecker.class, getClass());
+    }
+
+    @Test
+    public void testNoteOp() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    mAppOps.noteOp(\"foo\", 0, \"bar\", \"baz\", \"qux\");",
+                        "    mAppOps.noteOp(0, 0, \"bar\", \"baz\", \"qux\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteOp(1, 2, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteOp(\"foo\", 1, \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteOp(1);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testNoteOpNoThrow() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteOpNoThrow(0, 1, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteOpNoThrow(\"foo\", 1, \"bar\");",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testStartOp() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOp(0, 0, \"bar\", true);",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOp(1, 2, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOp(\"foo\", 1, \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOp(1);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testStartOpNoThrow() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOpNoThrow(0, 0, \"bar\", true);",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOpNoThrow(1, 2, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.startOpNoThrow(\"foo\", 1, \"bar\");",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testNoteProxyOp() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteProxyOp(1, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteProxyOp(\"foo\", \"bar\");",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testNoteProxyOpNoThrow() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\", 1);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testFinishOp() {
+        mCompilationHelper
+                .addSourceFile("/android/app/AppOpsManager.java")
+                .addSourceLines("Example.java",
+                        "import android.app.AppOpsManager;",
+                        "public class Example {",
+                        "  void example() {",
+                        "    AppOpsManager mAppOps = new AppOpsManager();",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.finishOp(1, 2, \"foo\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.finishOp(\"foo\", 1, \"bar\");",
+                        "    // BUG: Diagnostic contains:",
+                        "    mAppOps.finishOp(1);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+
+
+}
diff --git a/errorprone/tests/res/android/app/AppOpsManager.java b/errorprone/tests/res/android/app/AppOpsManager.java
new file mode 100644
index 0000000..216270c
--- /dev/null
+++ b/errorprone/tests/res/android/app/AppOpsManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+public class AppOpsManager {
+
+    public int noteOp(String op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOp(int op) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOp(int op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOp(String op, int uid, String packageName,
+            String attributionTag, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOp(int op, int uid, String packageName,
+            String attributionTag, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOpNoThrow(String op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteOpNoThrow(int op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOp(int op) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOp(int op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOp(String op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOpNoThrow(String op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOpNoThrow(int op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteProxyOp(String op, String proxiedPackageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteProxyOp(int op, String proxiedPackageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteProxyOpNoThrow(String op, String proxiedPackageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int noteProxyOpNoThrow(String op, String proxiedPackageName,
+            int proxiedUid) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void finishOp(int op) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void finishOp(String op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void finishOp(int op, int uid, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 93a336e..96b3325 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,7 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.text.FontConfig;
+import android.util.ArraySet;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -120,7 +122,23 @@
         }
     }
 
-    private static FontConfig readFamilies(
+    /**
+     * Parses the familyset tag in font.xml
+     * @param parser a XML pull parser
+     * @param fontDir A system font directory, e.g. "/system/fonts"
+     * @param customization A OEM font customization
+     * @param updatableFontMap A map of updated font files
+     * @param lastModifiedDate A date that the system font is updated.
+     * @param configVersion A version of system font config.
+     * @param allowNonExistingFile true if allowing non-existing font files during parsing fonts.xml
+     * @return result of fonts.xml
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     *
+     * @hide
+     */
+    public static FontConfig readFamilies(
             @NonNull XmlPullParser parser,
             @NonNull String fontDir,
             @NonNull FontCustomizationParser.Result customization,
@@ -159,7 +177,24 @@
         }
 
         families.addAll(oemNamedFamilies.values());
-        return new FontConfig(families, aliases, lastModifiedDate, configVersion);
+
+        // Filters aliases that point to non-existing families.
+        Set<String> namedFamilies = new ArraySet<>();
+        for (int i = 0; i < families.size(); ++i) {
+            String name = families.get(i).getName();
+            if (name != null) {
+                namedFamilies.add(name);
+            }
+        }
+        List<FontConfig.Alias> filtered = new ArrayList<>();
+        for (int i = 0; i < aliases.size(); ++i) {
+            FontConfig.Alias alias = aliases.get(i);
+            if (namedFamilies.contains(alias.getOriginal())) {
+                filtered.add(alias);
+            }
+        }
+
+        return new FontConfig(families, filtered, lastModifiedDate, configVersion);
     }
 
     private static boolean keepReading(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index fe04f0d..8bc3cae 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1106,6 +1106,43 @@
         ProcessInitializer.sInstance.setContext(context);
     }
 
+    /**
+     * Returns true if HardwareRender will produce output.
+     *
+     * This value is global to the process and affects all uses of HardwareRenderer,
+     * including
+     * those created by the system such as those used by the View tree when using hardware
+     * accelerated rendering.
+     *
+     * Default is true in all production environments, but may be false in testing-focused
+     * emulators or if {@link #setDrawingEnabled(boolean)} is used.
+     */
+    public static boolean isDrawingEnabled() {
+        return nIsDrawingEnabled();
+    }
+
+    /**
+     * Toggles whether or not HardwareRenderer will produce drawing output globally in the current
+     * process.
+     *
+     * This applies to all HardwareRenderer instances, including those created by the platform such
+     * as those used by the system for hardware accelerated View rendering.
+     *
+     * The capability to disable drawing output is intended for test environments, primarily
+     * headless ones. By setting this to false, tests that launch activities or interact with Views
+     * can be quicker with less RAM usage by skipping the final step of View drawing. All View
+     * lifecycle events will occur as normal, only the final step of rendering on the GPU to the
+     * display will be skipped.
+     *
+     * This can be toggled on and off at will, so screenshot tests can also run in this same
+     * environment by toggling drawing back on and forcing a frame to be drawn such as by calling
+     * view#invalidate(). Once drawn and the screenshot captured, this can then be turned back off.
+     */
+    // TODO(b/194195794): Add link to androidx's Screenshot library for help with this
+    public static void setDrawingEnabled(boolean drawingEnabled) {
+        nSetDrawingEnabled(drawingEnabled);
+    }
+
     private static final class DestroyContextRunnable implements Runnable {
         private final long mNativeInstance;
 
@@ -1438,4 +1475,8 @@
 
     private static native void nInitDisplayInfo(int width, int height, float refreshRate,
             int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
+
+    private static native void nSetDrawingEnabled(boolean drawingEnabled);
+
+    private static native boolean nIsDrawingEnabled();
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
index a0d5b00..cafc233 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -23,15 +23,19 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.window.common.DeviceStateManagerPostureProducer;
 import androidx.window.common.DisplayFeature;
 import androidx.window.common.ResourceConfigDisplayFeatureProducer;
 import androidx.window.common.SettingsDevicePostureProducer;
 import androidx.window.common.SettingsDisplayFeatureProducer;
+import androidx.window.extensions.organizer.SplitController;
 import androidx.window.util.DataProducer;
 import androidx.window.util.PriorityDataProducer;
 
@@ -56,6 +60,8 @@
     private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
     private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
 
+    private final SplitController mSplitController;
+
     SampleExtensionImpl(Context context) {
         mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
         mDevicePostureProducer = new PriorityDataProducer<>(List.of(
@@ -71,6 +77,8 @@
 
         mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
         mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
+
+        mSplitController = new SplitController();
     }
 
     private int getFeatureState(DisplayFeature feature) {
@@ -134,4 +142,28 @@
 
         onDisplayFeaturesChanged();
     }
+
+    @Override
+    public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) {
+        mSplitController.setSplitRules(splitRules);
+    }
+
+    @Override
+    @NonNull
+    public List<ExtensionSplitRule> getSplitRules() {
+        return new ArrayList<>(mSplitController.getSplitRules());
+    }
+
+    @Override
+    public void setSplitOrganizerCallback(@Nullable SplitOrganizerCallback callback) {
+        mSplitController.setSplitOrganizerCallback(callback);
+    }
+
+    @Override
+    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
+            @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule,
+            int startRequestId) {
+        mSplitController.startActivityToSide(launchingActivity, intent, options, splitPairRule,
+                startRequestId);
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
new file mode 100644
index 0000000..48d78cf
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.Activity;
+import android.app.WindowConfiguration.WindowingMode;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentCreationParams;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizer;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Platform default Extensions implementation of {@link TaskFragmentOrganizer} to organize
+ * task fragments.
+ *
+ * All calls into methods of this class are expected to be on the UI thread.
+ */
+class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
+
+    /** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
+    private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
+
+    /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
+    private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
+
+    /**
+     * Mapping from the client assigned unique token to the TaskFragment parent
+     * {@link Configuration}.
+     */
+    final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
+
+    private final TaskFragmentCallback mCallback;
+
+    /**
+     * Callback that notifies the controller about changes to task fragments.
+     */
+    interface TaskFragmentCallback {
+        void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+        void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
+        void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
+        void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
+                @NonNull Configuration parentConfig);
+    }
+
+    /**
+     * @param executor  callbacks from WM Core are posted on this executor. It should be tied to the
+     *                  UI thread that all other calls into methods of this class are also on.
+     */
+    JetpackTaskFragmentOrganizer(@NonNull Executor executor, TaskFragmentCallback callback) {
+        super(executor);
+        mCallback = callback;
+    }
+
+    /**
+     * Starts a new Activity and puts it into split with an existing Activity side-by-side.
+     * @param launchingFragmentToken    token for the launching TaskFragment. If it exists, it will
+     *                                  be resized based on {@param launchingFragmentBounds}.
+     *                                  Otherwise, we will create a new TaskFragment with the given
+     *                                  token for the {@param launchingActivity}.
+     * @param launchingFragmentBounds   the initial bounds for the launching TaskFragment.
+     * @param launchingActivity the Activity to put on the left hand side of the split as the
+     *                          primary.
+     * @param secondaryFragmentToken    token to create the secondary TaskFragment with.
+     * @param secondaryFragmentBounds   the initial bounds for the secondary TaskFragment
+     * @param activityIntent    Intent to start the secondary Activity with.
+     * @param activityOptions   ActivityOptions to start the secondary Activity with.
+     */
+    void startActivityToSide(IBinder launchingFragmentToken, Rect launchingFragmentBounds,
+            Activity launchingActivity, IBinder secondaryFragmentToken,
+            Rect secondaryFragmentBounds,  Intent activityIntent,
+            @Nullable Bundle activityOptions) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final IBinder ownerToken = launchingActivity.getActivityToken();
+
+        // Create or resize the launching TaskFragment.
+        if (mFragmentInfos.containsKey(launchingFragmentToken)) {
+            resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds);
+        } else {
+            createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
+                    launchingFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, launchingActivity);
+        }
+
+        // Create a TaskFragment for the secondary activity.
+        createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken,
+                secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent,
+                activityOptions);
+
+        applyTransaction(wct);
+    }
+
+    /**
+     * Expands an existing TaskFragment to fill parent.
+     * @param wct WindowContainerTransaction in which the task fragment should be resized.
+     * @param fragmentToken token of an existing TaskFragment.
+     */
+    void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
+        resizeTaskFragment(wct, fragmentToken, new Rect());
+    }
+
+    /**
+     * Expands an existing TaskFragment to fill parent.
+     * @param fragmentToken token of an existing TaskFragment.
+     */
+    void expandTaskFragment(IBinder fragmentToken) {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        expandTaskFragment(wct, fragmentToken);
+        applyTransaction(wct);
+    }
+
+    /**
+     * Expands an Activity to fill parent by moving it to a new TaskFragment.
+     * @param fragmentToken token to create new TaskFragment with.
+     * @param activity      activity to move to the fill-parent TaskFragment.
+     */
+    void expandActivity(IBinder fragmentToken, Activity activity) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        createTaskFragmentAndReparentActivity(
+                wct, fragmentToken, activity.getActivityToken(), new Rect(),
+                WINDOWING_MODE_UNDEFINED, activity);
+        applyTransaction(wct);
+    }
+
+    /**
+     * @param ownerToken The token of the activity that creates this task fragment. It does not
+     *                   have to be a child of this task fragment, but must belong to the same task.
+     */
+    private void createTaskFragmentAndReparentActivity(
+            WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
+            @NonNull Rect bounds, @WindowingMode int windowingMode, Activity activity) {
+        final TaskFragmentCreationParams fragmentOptions =
+                createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
+        wct.createTaskFragment(fragmentOptions)
+                .reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken());
+    }
+
+    /**
+     * @param ownerToken The token of the activity that creates this task fragment. It does not
+     *                   have to be a child of this task fragment, but must belong to the same task.
+     */
+    private void createTaskFragmentAndStartActivity(
+            WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
+            @NonNull Rect bounds, @WindowingMode int windowingMode, Intent activityIntent,
+            @Nullable Bundle activityOptions) {
+        final TaskFragmentCreationParams fragmentOptions =
+                createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
+        wct.createTaskFragment(fragmentOptions)
+                .startActivityInTaskFragment(fragmentToken, activityIntent, activityOptions);
+    }
+
+    TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
+            Rect bounds, @WindowingMode int windowingMode) {
+        if (mFragmentInfos.containsKey(fragmentToken)) {
+            throw new IllegalArgumentException(
+                    "There is an existing TaskFragment with fragmentToken=" + fragmentToken);
+        }
+
+        return new TaskFragmentCreationParams.Builder(
+                getIOrganizer(),
+                fragmentToken,
+                ownerToken)
+                .setInitialBounds(bounds)
+                .setWindowingMode(windowingMode)
+                .build();
+    }
+
+    void resizeTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken,
+            @Nullable Rect bounds) {
+        if (!mFragmentInfos.containsKey(fragmentToken)) {
+            throw new IllegalArgumentException(
+                    "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
+        }
+        if (bounds == null) {
+            bounds = new Rect();
+        }
+        wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds);
+    }
+
+    void deleteTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
+        if (!mFragmentInfos.containsKey(fragmentToken)) {
+            throw new IllegalArgumentException(
+                    "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
+        }
+        wct.deleteTaskFragment(mFragmentInfos.get(fragmentToken).getToken());
+    }
+
+    @Override
+    public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
+        final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
+        final IBinder fragmentToken = info.getFragmentToken();
+        final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
+        mFragmentInfos.put(fragmentToken, info);
+        mFragmentLeashes.put(fragmentToken, leash);
+
+        if (mCallback != null) {
+            mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+        }
+    }
+
+    @Override
+    public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+        mFragmentInfos.put(fragmentToken, taskFragmentInfo);
+
+        if (mCallback != null) {
+            mCallback.onTaskFragmentInfoChanged(taskFragmentInfo);
+        }
+    }
+
+    @Override
+    public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
+        mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
+        mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
+
+        if (mCallback != null) {
+            mCallback.onTaskFragmentVanished(taskFragmentInfo);
+        }
+    }
+
+    @Override
+    public void onTaskFragmentParentInfoChanged(
+            @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
+        mFragmentParentConfigs.put(fragmentToken, parentConfig);
+
+        if (mCallback != null) {
+            mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
new file mode 100644
index 0000000..ade8573
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+
+import androidx.window.extensions.ExtensionSplitPairRule;
+
+/**
+ * Client-side descriptor of a split that holds two containers.
+ */
+class SplitContainer {
+    private final TaskFragmentContainer mPrimaryContainer;
+    private final TaskFragmentContainer mSecondaryContainer;
+    private final ExtensionSplitPairRule mSplitPairRule;
+
+    SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
+            @NonNull Activity primaryActivity,
+            @NonNull TaskFragmentContainer secondaryContainer,
+            @NonNull ExtensionSplitPairRule splitPairRule) {
+        mPrimaryContainer = primaryContainer;
+        mSecondaryContainer = secondaryContainer;
+        mSplitPairRule = splitPairRule;
+
+        if (mSplitPairRule.finishPrimaryWithSecondary || mSplitPairRule.useAsPlaceholder) {
+            mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
+        }
+        if (mSplitPairRule.finishSecondaryWithPrimary || mSplitPairRule.useAsPlaceholder) {
+            mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
+        }
+    }
+
+    @NonNull
+    TaskFragmentContainer getPrimaryContainer() {
+        return mPrimaryContainer;
+    }
+
+    @NonNull
+    TaskFragmentContainer getSecondaryContainer() {
+        return mSecondaryContainer;
+    }
+
+    @NonNull
+    ExtensionSplitPairRule getSplitPairRule() {
+        return mSplitPairRule;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
new file mode 100644
index 0000000..7298d34
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityClient;
+import android.app.ActivityThread;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.window.extensions.ExtensionInterface.SplitOrganizerCallback;
+import androidx.window.extensions.ExtensionSplitActivityRule;
+import androidx.window.extensions.ExtensionSplitInfo;
+import androidx.window.extensions.ExtensionSplitPairRule;
+import androidx.window.extensions.ExtensionSplitRule;
+import androidx.window.extensions.ExtensionTaskFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Main controller class that manages split states and presentation.
+ */
+public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback {
+
+    private final SplitPresenter mPresenter;
+
+    // Currently applied split configuration.
+    private final List<ExtensionSplitRule> mSplitRules = new ArrayList<>();
+    private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+    private final List<SplitContainer> mSplitContainers = new ArrayList<>();
+
+    // Callback to Jetpack to notify about changes to split states.
+    private SplitOrganizerCallback mSplitOrganizerCallback;
+
+    public SplitController() {
+        mPresenter = new SplitPresenter(ActivityThread.currentActivityThread().getExecutor(),
+                this);
+        // Register a callback to be notified about activities being created.
+        ActivityThread.currentActivityThread().getApplication().registerActivityLifecycleCallbacks(
+                new LifecycleCallbacks());
+    }
+
+    public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) {
+        mSplitRules.clear();
+        mSplitRules.addAll(splitRules);
+    }
+
+    @NonNull
+    public List<ExtensionSplitRule> getSplitRules() {
+        return mSplitRules;
+    }
+
+    /**
+     * Starts an activity to side of the launchingActivity with the provided split config.
+     */
+    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
+            @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule,
+            int startRequestId) {
+        try {
+            mPresenter.startActivityToSide(launchingActivity, intent, options, splitPairRule);
+        } catch (Exception e) {
+            if (mSplitOrganizerCallback != null && startRequestId != -1) {
+                mSplitOrganizerCallback.onActivityFailedToStartInContainer(startRequestId, e);
+            }
+        }
+    }
+
+    /**
+     * Registers the split organizer callback to notify about changes to active splits.
+     */
+    public void setSplitOrganizerCallback(@NonNull SplitOrganizerCallback callback) {
+        mSplitOrganizerCallback = callback;
+        updateCallbackIfNecessary();
+    }
+
+    @Override
+    public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.getTaskFragmentToken().equals(
+                    taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken())) {
+                container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.getTaskFragmentToken().equals(taskFragmentInfo.getFragmentToken())) {
+                container.setInfo(taskFragmentInfo);
+
+                if (taskFragmentInfo.isEmpty()) {
+                    cleanupContainer(container, true /* shouldFinishDependent */);
+                    updateCallbackIfNecessary();
+                }
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.getTaskFragmentToken().equals(taskFragmentInfo.getFragmentToken())) {
+                cleanupContainer(container, true /* shouldFinishDependent */);
+                updateCallbackIfNecessary();
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
+            @NonNull Configuration parentConfig) {
+        TaskFragmentContainer container = getContainer(fragmentToken);
+        if (container != null) {
+            mPresenter.updateContainer(container);
+            updateCallbackIfNecessary();
+        }
+    }
+
+    /**
+     * Checks if the activity start should be routed to a particular container. It can create a new
+     * container for the activity and a new split container if necessary.
+     */
+    void onActivityCreated(@NonNull Activity launchedActivity) {
+        final ComponentName componentName = launchedActivity.getComponentName();
+
+        final List<ExtensionSplitRule> splitRules = getSplitRules();
+        final TaskFragmentContainer currentContainer = getContainerWithActivity(
+                launchedActivity.getActivityToken());
+
+        // Check if the activity is configured to always be expanded.
+        if (shouldExpand(componentName, splitRules)) {
+            if (shouldContainerBeExpanded(currentContainer)) {
+                // Make sure that the existing container is expanded
+                mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
+            } else {
+                // Put activity into a new expanded container
+                final TaskFragmentContainer newContainer = newContainer(launchedActivity);
+                mPresenter.expandActivity(newContainer.getTaskFragmentToken(),
+                        launchedActivity);
+            }
+            return;
+        }
+
+        // Check if activity requires a placeholder
+        if (launchPlaceholderIfNecessary(launchedActivity)) {
+            return;
+        }
+
+        // TODO(b/190433398): Check if it is a placeholder and there is already another split
+        // created by the primary activity. This is necessary for the case when the primary activity
+        // launched another secondary in the split, but the placeholder was still launched by the
+        // logic above. We didn't prevent the placeholder launcher because we didn't know that
+        // another secondary activity is coming up.
+
+        // Check if the activity should form a split with the activity below in the same task
+        // fragment.
+        Activity activityBelow = null;
+        if (currentContainer != null) {
+            final List<Activity> containerActivities = currentContainer.collectActivities();
+            final int index = containerActivities.indexOf(launchedActivity);
+            if (index > 0) {
+                activityBelow = containerActivities.get(index - 1);
+            }
+        }
+        if (activityBelow == null) {
+            IBinder belowToken = ActivityClient.getInstance().getActivityTokenBelow(
+                    launchedActivity.getActivityToken());
+            if (belowToken != null) {
+                activityBelow = ActivityThread.currentActivityThread().getActivity(belowToken);
+            }
+        }
+        if (activityBelow == null) {
+            return;
+        }
+
+        final ExtensionSplitPairRule splitPairRule = getSplitRule(
+                activityBelow.getComponentName(), componentName, splitRules);
+        if (splitPairRule == null) {
+            return;
+        }
+
+        mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
+                splitPairRule);
+
+        updateCallbackIfNecessary();
+    }
+
+    /**
+     * Returns a container that this activity is registered with. An activity can only belong to one
+     * container, or no container at all.
+     */
+    @Nullable
+    TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.hasActivity(activityToken)) {
+                return container;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates and registers a new organized container with an optional activity that will be
+     * re-parented to it in a WCT.
+     */
+    TaskFragmentContainer newContainer(@Nullable Activity activity) {
+        TaskFragmentContainer container = new TaskFragmentContainer(activity);
+        mContainers.add(container);
+        return container;
+    }
+
+    /**
+     * Creates and registers a new split with the provided containers and configuration.
+     */
+    void registerSplit(@NonNull TaskFragmentContainer primaryContainer,
+            @NonNull Activity primaryActivity,
+            @NonNull TaskFragmentContainer secondaryContainer,
+            @NonNull ExtensionSplitPairRule splitPairRule) {
+        SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+                secondaryContainer, splitPairRule);
+        mSplitContainers.add(splitContainer);
+    }
+
+    void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
+        if (container.isFinished()) {
+            return;
+        }
+
+        container.finish(shouldFinishDependent);
+
+        // Remove all split containers that included this one
+        mContainers.remove(container);
+        List<SplitContainer> containersToRemove = new ArrayList<>();
+        for (SplitContainer splitContainer : mSplitContainers) {
+            if (container.equals(splitContainer.getSecondaryContainer())
+                    || container.equals(splitContainer.getPrimaryContainer())) {
+                containersToRemove.add(splitContainer);
+            }
+        }
+        mSplitContainers.removeAll(containersToRemove);
+
+        mPresenter.deleteContainer(container);
+    }
+
+    /**
+     * Returns the topmost not finished container.
+     */
+    @Nullable
+    TaskFragmentContainer getTopActiveContainer() {
+        for (int i = mContainers.size() - 1; i >= 0; i--) {
+            TaskFragmentContainer container = mContainers.get(i);
+            if (!container.isFinished()) {
+                return container;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Updates the presentation of the container. If the container is part of the split or should
+     * have a placeholder, it will also update the other part of the split.
+     */
+    void updateContainer(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer container) {
+        if (launchPlaceholderIfNecessary(container)) {
+            // Placeholder was launched, the positions will be updated when the activity is added
+            // to the secondary container.
+            return;
+        }
+        if (shouldContainerBeExpanded(container)) {
+            if (container.getInfo() != null) {
+                mPresenter.expandTaskFragment(wct, container.getTaskFragmentToken());
+            }
+            // If the info is not available yet the task fragment will be expanded when it's ready
+            return;
+        }
+        SplitContainer splitContainer = getActiveSplitForContainer(container);
+        if (splitContainer == null) {
+            return;
+        }
+        if (splitContainer != mSplitContainers.get(mSplitContainers.size() - 1)) {
+            // Skip position update - it isn't the topmost split.
+            return;
+        }
+        if (splitContainer.getPrimaryContainer().isEmpty()
+                || splitContainer.getSecondaryContainer().isEmpty()) {
+            // Skip position update - one or both containers are empty.
+            return;
+        }
+        if (dismissPlaceholderIfNecessary(splitContainer)) {
+            // Placeholder was finished, the positions will be updated when its container is emptied
+            return;
+        }
+        mPresenter.updateSplitContainer(splitContainer, container, wct);
+    }
+
+    /**
+     * Returns the top active split container that has the provided container.
+     */
+    @Nullable
+    private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) {
+        for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
+            SplitContainer splitContainer = mSplitContainers.get(i);
+            if (container.equals(splitContainer.getSecondaryContainer())
+                    || container.equals(splitContainer.getPrimaryContainer())) {
+                return splitContainer;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Checks if the container requires a placeholder and launches it if necessary.
+     */
+    private boolean launchPlaceholderIfNecessary(@NonNull TaskFragmentContainer container) {
+        final Activity topActivity = container.getTopNonFinishingActivity();
+        if (topActivity == null) {
+            return false;
+        }
+
+        return launchPlaceholderIfNecessary(topActivity);
+    }
+
+    boolean launchPlaceholderIfNecessary(@NonNull Activity activity) {
+        final  TaskFragmentContainer container = getContainerWithActivity(
+                activity.getActivityToken());
+
+        SplitContainer splitContainer = container != null ? getActiveSplitForContainer(container)
+                : null;
+        if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) {
+            // Don't launch placeholder in primary split container
+            return false;
+        }
+
+        // Check if there is enough space for launch
+        final ExtensionSplitPairRule placeholderRule = getPlaceholderRule(
+                activity.getComponentName());
+        if (placeholderRule == null || !mPresenter.shouldShowSideBySide(
+                mPresenter.getParentContainerBounds(activity), placeholderRule)) {
+            return false;
+        }
+
+        Intent placeholderIntent = new Intent();
+        placeholderIntent.setComponent(placeholderRule.secondaryActivityName);
+        // TODO(b/190433398): Handle failed request
+        startActivityToSide(activity, placeholderIntent, null, placeholderRule, -1);
+        return true;
+    }
+
+    private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
+        if (!splitContainer.getSplitPairRule().useAsPlaceholder) {
+            return false;
+        }
+
+        if (mPresenter.shouldShowSideBySide(splitContainer)) {
+            return false;
+        }
+
+        cleanupContainer(splitContainer.getSecondaryContainer(),
+                false /* shouldFinishDependent */);
+        return true;
+    }
+
+    /**
+     * Returns the rule to launch a placeholder for the activity with the provided component name
+     * if it is configured in the split config.
+     */
+    private ExtensionSplitPairRule getPlaceholderRule(@NonNull ComponentName componentName) {
+        for (ExtensionSplitRule rule : mSplitRules) {
+            if (!(rule instanceof ExtensionSplitPairRule)) {
+                continue;
+            }
+            ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule;
+            if (componentName.equals(pairRule.primaryActivityName)
+                    && pairRule.useAsPlaceholder) {
+                return pairRule;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Notifies listeners about changes to split states if necessary.
+     */
+    private void updateCallbackIfNecessary() {
+        if (mSplitOrganizerCallback == null) {
+            return;
+        }
+        // TODO(b/190433398): Check if something actually changed
+        mSplitOrganizerCallback.onSplitInfoChanged(getActiveSplitStates());
+    }
+
+    /**
+     * Returns a list of descriptors for currently active split states.
+     */
+    private List<ExtensionSplitInfo> getActiveSplitStates() {
+        List<ExtensionSplitInfo> splitStates = new ArrayList<>();
+        for (SplitContainer container : mSplitContainers) {
+            ExtensionTaskFragment primaryContainer =
+                    new ExtensionTaskFragment(
+                            container.getPrimaryContainer().collectActivities());
+            ExtensionTaskFragment secondaryContainer =
+                    new ExtensionTaskFragment(
+                            container.getSecondaryContainer().collectActivities());
+            ExtensionSplitInfo splitState = new ExtensionSplitInfo(primaryContainer,
+                    secondaryContainer, container.getSplitPairRule().splitRatio);
+            splitStates.add(splitState);
+        }
+        return splitStates;
+    }
+
+    /**
+     * Returns {@code true} if the container is expanded to occupy full task size.
+     * Returns {@code false} if the container is included in an active split.
+     */
+    boolean shouldContainerBeExpanded(@Nullable TaskFragmentContainer container) {
+        if (container == null) {
+            return false;
+        }
+        for (SplitContainer splitContainer : mSplitContainers) {
+            if (container.equals(splitContainer.getPrimaryContainer())
+                    || container.equals(splitContainer.getSecondaryContainer())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns a split rule for the provided pair of component names if available.
+     */
+    @Nullable
+    private static ExtensionSplitPairRule getSplitRule(@NonNull ComponentName primaryActivityName,
+            @NonNull ComponentName secondaryActivityName,
+            @NonNull List<ExtensionSplitRule> splitRules) {
+        if (splitRules == null || primaryActivityName == null || secondaryActivityName == null) {
+            return null;
+        }
+
+        for (ExtensionSplitRule rule : splitRules) {
+            if (!(rule instanceof ExtensionSplitPairRule)) {
+                continue;
+            }
+            ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule;
+            if (match(secondaryActivityName, pairRule.secondaryActivityName)
+                    && match(primaryActivityName, pairRule.primaryActivityName)) {
+                return pairRule;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.getTaskFragmentToken().equals(fragmentToken)) {
+                return container;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if an Activity with the provided component name should always be
+     * expanded to occupy full task bounds. Such activity must not be put in a split.
+     */
+    private static boolean shouldExpand(@NonNull ComponentName componentName,
+            List<ExtensionSplitRule> splitRules) {
+        if (splitRules == null) {
+            return false;
+        }
+        for (ExtensionSplitRule rule : splitRules) {
+            if (!(rule instanceof ExtensionSplitActivityRule)) {
+                continue;
+            }
+            ExtensionSplitActivityRule activityRule = (ExtensionSplitActivityRule) rule;
+            if (match(componentName, activityRule.activityName)
+                    && activityRule.alwaysExpand) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Match check allowing wildcards for activity class name but not package name. */
+    private static boolean match(@NonNull ComponentName activityComponent,
+            @NonNull ComponentName ruleComponent) {
+        if (activityComponent.toString().contains("*")) {
+            throw new IllegalArgumentException("Wildcard can only be part of the rule.");
+        }
+        final boolean packagesMatch =
+                activityComponent.getPackageName().equals(ruleComponent.getPackageName());
+        final boolean classesMatch =
+                activityComponent.getClassName().equals(ruleComponent.getClassName());
+        return packagesMatch && (classesMatch
+                || wildcardMatch(activityComponent.getClassName(), ruleComponent.getClassName()));
+    }
+
+    /**
+     * Checks if the provided name matches the pattern.
+     */
+    private static boolean wildcardMatch(@NonNull String name, @NonNull String pattern) {
+        if (!pattern.contains("*")) {
+            return false;
+        }
+        if (pattern.equals("*")) {
+            return true;
+        }
+        if (pattern.indexOf("*") != pattern.lastIndexOf("*") || !pattern.endsWith("*")) {
+            throw new IllegalArgumentException(
+                    "Name pattern with a wildcard must only contain a single * in the end");
+        }
+        return name.startsWith(pattern.substring(0, pattern.length() - 1));
+    }
+
+    private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+            // Calling after Activity#onCreate is complete to allow the app launch something
+            // first. In case of a configured placeholder activity we want to make sure
+            // that we don't launch it if an activity itself already requested something to be
+            // launched to side.
+            SplitController.this.onActivityCreated(activity);
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+        }
+
+        @Override
+        public void onActivityResumed(Activity activity) {
+        }
+
+        @Override
+        public void onActivityPaused(Activity activity) {
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
new file mode 100644
index 0000000..381d6d7
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.window.TaskFragmentCreationParams;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.ExtensionSplitPairRule;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Controls the visual presentation of the splits according to the containers formed by
+ * {@link SplitController}.
+ */
+class SplitPresenter extends JetpackTaskFragmentOrganizer {
+    private static final int POSITION_LEFT = 0;
+    private static final int POSITION_RIGHT = 1;
+    private static final int POSITION_FILL = 2;
+
+    @IntDef(value = {
+            POSITION_LEFT,
+            POSITION_RIGHT,
+            POSITION_FILL,
+    })
+    private @interface Position {}
+
+    private final SplitController mController;
+
+    SplitPresenter(@NonNull Executor executor, SplitController controller) {
+        super(executor, controller);
+        mController = controller;
+        registerOrganizer();
+    }
+
+    /**
+     * Updates the presentation of the provided container.
+     */
+    void updateContainer(TaskFragmentContainer container) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mController.updateContainer(wct, container);
+        applyTransaction(wct);
+    }
+
+    /**
+     * Deletes the provided container and updates the presentation if necessary.
+     */
+    void deleteContainer(TaskFragmentContainer container) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        deleteTaskFragment(wct, container.getTaskFragmentToken());
+
+        final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer();
+        if (newTopContainer != null) {
+            mController.updateContainer(wct, newTopContainer);
+        }
+
+        applyTransaction(wct);
+    }
+
+    /**
+     * Creates a new split container with the two provided activities.
+     * @param primaryActivity An activity that should be in the primary container. If it is not
+     *                        currently in an existing container, a new one will be created and the
+     *                        activity will be re-parented to it.
+     * @param secondaryActivity An activity that should be in the secondary container. If it is not
+     *                          currently in an existing container, or if it is currently in the
+     *                          same container as the primary activity, a new container will be
+     *                          created and the activity will be re-parented to it.
+     * @param rule The split rule to be applied to the container.
+     */
+    void createNewSplitContainer(@NonNull Activity primaryActivity,
+            @NonNull Activity secondaryActivity, @NonNull ExtensionSplitPairRule rule) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        final Rect parentBounds = getParentContainerBounds(primaryActivity);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+
+        TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
+                primaryActivity.getActivityToken());
+        if (primaryContainer == null) {
+            primaryContainer = mController.newContainer(primaryActivity);
+
+            final TaskFragmentCreationParams fragmentOptions =
+                    createFragmentOptions(
+                            primaryContainer.getTaskFragmentToken(),
+                            primaryActivity.getActivityToken(),
+                            primaryRectBounds,
+                            WINDOWING_MODE_MULTI_WINDOW);
+            wct.createTaskFragment(fragmentOptions);
+
+            wct.reparentActivityToTaskFragment(primaryContainer.getTaskFragmentToken(),
+                    primaryActivity.getActivityToken());
+        } else {
+            resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
+        }
+
+        TaskFragmentContainer secondaryContainer = mController.getContainerWithActivity(
+                secondaryActivity.getActivityToken());
+        if (secondaryContainer == null || secondaryContainer == primaryContainer) {
+            secondaryContainer = mController.newContainer(secondaryActivity);
+
+            final TaskFragmentCreationParams fragmentOptions =
+                    createFragmentOptions(
+                            secondaryContainer.getTaskFragmentToken(),
+                            secondaryActivity.getActivityToken(),
+                            secondaryRectBounds,
+                            WINDOWING_MODE_MULTI_WINDOW);
+            wct.createTaskFragment(fragmentOptions);
+
+            wct.reparentActivityToTaskFragment(secondaryContainer.getTaskFragmentToken(),
+                    secondaryActivity.getActivityToken());
+        } else {
+            resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
+        }
+
+        // TODO(b/190433398): The primary container and the secondary container should also be set
+        // as adjacent (WCT#setAdjacentRoots) to make activities behind invisible.
+        applyTransaction(wct);
+
+        mController.registerSplit(primaryContainer, primaryActivity, secondaryContainer, rule);
+    }
+
+    /**
+     * Starts a new activity to the side, creating a new split container. A new container will be
+     * created for the activity that will be started.
+     * @param launchingActivity An activity that should be in the primary container. If it is not
+     *                          currently in an existing container, a new one will be created and
+     *                          the activity will be re-parented to it.
+     * @param activityIntent The intent to start the new activity.
+     * @param activityOptions The options to apply to new activity start.
+     * @param rule The split rule to be applied to the container.
+     */
+    void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
+            @Nullable Bundle activityOptions, @NonNull ExtensionSplitPairRule rule) {
+        final Rect parentBounds = getParentContainerBounds(launchingActivity);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+
+        TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
+                launchingActivity.getActivityToken());
+        if (primaryContainer == null) {
+            primaryContainer = mController.newContainer(launchingActivity);
+        }
+
+        TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+        startActivityToSide(
+                primaryContainer.getTaskFragmentToken(),
+                primaryRectBounds,
+                launchingActivity,
+                secondaryContainer.getTaskFragmentToken(),
+                secondaryRectBounds,
+                activityIntent,
+                activityOptions);
+
+        // TODO(b/190433398): The primary container and the secondary container should also be set
+        // as adjacent (WCT#setAdjacentRoots) to make activities behind invisible.
+
+        mController.registerSplit(primaryContainer, launchingActivity, secondaryContainer,
+                rule);
+    }
+
+    /**
+     * Updates the positions of containers in an existing split.
+     * @param splitContainer The split container to be updated.
+     * @param updatedContainer The task fragment that was updated and caused this split update.
+     * @param wct WindowContainerTransaction that this update should be performed with.
+     */
+    void updateSplitContainer(@NonNull SplitContainer splitContainer,
+            @NonNull TaskFragmentContainer updatedContainer,
+            @NonNull WindowContainerTransaction wct) {
+        // Getting the parent bounds using the updated container - it will have the recent value.
+        final Rect parentBounds = getParentContainerBounds(updatedContainer);
+        final ExtensionSplitPairRule rule = splitContainer.getSplitPairRule();
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+
+        // TODO(b/190433398): Check if the bounds actually changed.
+        // If the task fragments are not registered yet, the positions will be updated after they
+        // are created again.
+        resizeTaskFragmentIfRegistered(wct, splitContainer.getPrimaryContainer(),
+                primaryRectBounds);
+        resizeTaskFragmentIfRegistered(wct, splitContainer.getSecondaryContainer(),
+                secondaryRectBounds);
+    }
+
+    /**
+     * Resizes the task fragment if it was already registered. Skips the operation if the container
+     * creation has not been reported from the server yet.
+     */
+    // TODO(b/190433398): Handle resize if the fragment hasn't appeared yet.
+    void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer container,
+            @Nullable Rect bounds) {
+        if (container.getInfo() == null) {
+            return;
+        }
+        // TODO(b/190433398): Check if the bounds actually changed.
+        resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+    }
+
+    boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
+        final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
+        return shouldShowSideBySide(parentBounds, splitContainer.getSplitPairRule());
+    }
+
+    boolean shouldShowSideBySide(@Nullable Rect parentBounds,
+            @NonNull ExtensionSplitPairRule rule) {
+        return parentBounds != null && parentBounds.width() >= rule.minWidth
+                // TODO(b/190433398): Consider proper smallest width computation.
+                && Math.min(parentBounds.width(), parentBounds.height()) >= rule.minSmallestWidth;
+    }
+
+    @NonNull
+    private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
+            @NonNull ExtensionSplitPairRule rule) {
+        if (!shouldShowSideBySide(parentBounds, rule)) {
+            return new Rect();
+        }
+
+        float splitRatio = rule.splitRatio;
+        switch (position) {
+            case POSITION_LEFT:
+                return new Rect(
+                        parentBounds.left,
+                        parentBounds.top,
+                        (int) (parentBounds.left + parentBounds.width() * splitRatio),
+                        parentBounds.bottom);
+            case POSITION_RIGHT:
+                return new Rect(
+                        (int) (parentBounds.left + parentBounds.width() * splitRatio),
+                        parentBounds.top,
+                        parentBounds.right,
+                        parentBounds.bottom);
+            case POSITION_FILL:
+                return parentBounds;
+        }
+        return parentBounds;
+    }
+
+    @NonNull
+    Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
+        final Configuration parentConfig = mFragmentParentConfigs.get(
+                container.getTaskFragmentToken());
+        if (parentConfig != null) {
+            return parentConfig.windowConfiguration.getBounds();
+        }
+
+        // If there is no parent yet - then assuming that activities are running in full task bounds
+        final Activity topActivity = container.getTopNonFinishingActivity();
+        final Rect bounds = topActivity != null ? getParentContainerBounds(topActivity) : null;
+
+        if (bounds == null) {
+            throw new IllegalStateException("Unknown parent bounds");
+        }
+        return bounds;
+    }
+
+    @NonNull
+    Rect getParentContainerBounds(@NonNull Activity activity) {
+        final TaskFragmentContainer container = mController.getContainerWithActivity(
+                activity.getActivityToken());
+        if (container != null) {
+            final Configuration parentConfig = mFragmentParentConfigs.get(
+                    container.getTaskFragmentToken());
+            if (parentConfig != null) {
+                return parentConfig.windowConfiguration.getBounds();
+            }
+        }
+
+        // TODO(b/190433398): Check if the client-side available info about parent bounds is enough.
+        if (!activity.isInMultiWindowMode()) {
+            // In fullscreen mode the max bounds should correspond to the task bounds.
+            return activity.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+        }
+        return activity.getResources().getConfiguration().windowConfiguration.getBounds();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
new file mode 100644
index 0000000..3cf37a6
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.os.Binder;
+import android.os.IBinder;
+import android.window.TaskFragmentInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Client-side container for a stack of activities. Corresponds to an instance of TaskFragment
+ * on the server side.
+ */
+class TaskFragmentContainer {
+    /**
+     * Client-created token that uniquely identifies the task fragment container instance.
+     */
+    @NonNull
+    private final IBinder mToken;
+
+    /**
+     * Server-provided task fragment information.
+     */
+    private TaskFragmentInfo mInfo;
+
+    /**
+     * Activity that is being reparented to this container, but haven't been added to {@link #mInfo}
+     * yet.
+     */
+    private Activity mReparentingActivity;
+
+    /** Containers that are dependent on this one and should be completely destroyed on exit. */
+    private final List<TaskFragmentContainer> mContainersToFinishOnExit =
+            new ArrayList<>();
+
+    /** Individual associated activities in different containers that should be finished on exit. */
+    private final List<Activity> mActivitiesToFinishOnExit = new ArrayList<>();
+
+    /** Indicates whether the container was cleaned up after the last activity was removed. */
+    private boolean mIsFinished;
+
+    /**
+     * Creates a container with an existing activity that will be re-parented to it in a window
+     * container transaction.
+     */
+    TaskFragmentContainer(@Nullable Activity activity) {
+        mToken = new Binder("TaskFragmentContainer");
+        mReparentingActivity = activity;
+    }
+
+    /**
+     * Returns the client-created token that uniquely identifies this container.
+     */
+    @NonNull
+    IBinder getTaskFragmentToken() {
+        return mToken;
+    }
+
+    /** List of activities that belong to this container and live in this process. */
+    @NonNull
+    List<Activity> collectActivities() {
+        // Add the re-parenting activity, in case the server has not yet reported the task
+        // fragment info update with it placed in this container. We still want to apply rules
+        // in this intermediate state.
+        List<Activity> allActivities = new ArrayList<>();
+        if (mReparentingActivity != null) {
+            allActivities.add(mReparentingActivity);
+        }
+        // Add activities reported from the server.
+        if (mInfo == null) {
+            return allActivities;
+        }
+        ActivityThread activityThread = ActivityThread.currentActivityThread();
+        for (IBinder token : mInfo.getActivities()) {
+            Activity activity = activityThread.getActivity(token);
+            if (activity != null && !allActivities.contains(activity)) {
+                allActivities.add(activity);
+            }
+        }
+        return allActivities;
+    }
+
+    boolean hasActivity(@NonNull IBinder token) {
+        if (mInfo != null && mInfo.getActivities().contains(token)) {
+            return true;
+        }
+        return mReparentingActivity != null
+                && mReparentingActivity.getActivityToken().equals(token);
+    }
+
+    @Nullable
+    TaskFragmentInfo getInfo() {
+        return mInfo;
+    }
+
+    void setInfo(@Nullable TaskFragmentInfo info) {
+        mInfo = info;
+        if (mInfo == null || mReparentingActivity == null) {
+            return;
+        }
+        // Cleanup activities that were being re-parented
+        for (IBinder activityToken : mInfo.getActivities()) {
+            if (mReparentingActivity.getActivityToken().equals(activityToken)) {
+                mReparentingActivity = null;
+                break;
+            }
+        }
+    }
+
+    @Nullable
+    Activity getTopNonFinishingActivity() {
+        List<Activity> activities = collectActivities();
+        if (activities.isEmpty()) {
+            return null;
+        }
+        int i = activities.size() - 1;
+        while (i >= 0 && activities.get(i).isFinishing()) {
+            i--;
+        }
+        return i >= 0 ? activities.get(i) : null;
+    }
+
+    boolean isEmpty() {
+        return mReparentingActivity == null && (mInfo == null || mInfo.isEmpty());
+    }
+
+    /**
+     * Adds a container that should be finished when this container is finished.
+     */
+    void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
+        mContainersToFinishOnExit.add(containerToFinish);
+    }
+
+    /**
+     * Adds an activity that should be finished when this container is finished.
+     */
+    void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
+        mActivitiesToFinishOnExit.add(activityToFinish);
+    }
+
+    /**
+     * Removes all activities that belong to this process and finishes other containers/activities
+     * configured to finish together.
+     */
+    void finish(boolean shouldFinishDependent) {
+        if (mIsFinished) {
+            return;
+        }
+        mIsFinished = true;
+
+        // Finish own activities
+        for (Activity activity : collectActivities()) {
+            activity.finish();
+        }
+
+        if (!shouldFinishDependent) {
+            return;
+        }
+
+        // Finish dependent containers
+        for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+            container.finish(true /* shouldFinishDependent */);
+        }
+        mContainersToFinishOnExit.clear();
+
+        // Finish associated activities
+        for (Activity activity : mActivitiesToFinishOnExit) {
+            activity.finish();
+        }
+        mActivitiesToFinishOnExit.clear();
+
+        // Finish activities that were being re-parented to this container.
+        if (mReparentingActivity != null) {
+            mReparentingActivity.finish();
+            mReparentingActivity = null;
+        }
+    }
+
+    boolean isFinished() {
+        return mIsFinished;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index be6652d..fdbc5f6 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
new file mode 100644
index 0000000..4e2a77f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.wm.shell.splitscreen.OutlineRoot
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.android.wm.shell.splitscreen.OutlineView
+        android:id="@+id/split_outline"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+
+</com.android.wm.shell.splitscreen.OutlineRoot>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index ea634cf..69aa31e 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Hou vas"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+    <string name="got_it" msgid="4428750913636945527">"Het dit"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index e4628d7..c754e3ca 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ወደ ቀጣይ ዝለል"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ወደ ቀዳሚ ዝለል"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+    <string name="got_it" msgid="4428750913636945527">"ገባኝ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7b5bda7..ac72a3d 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"إخفاء"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+    <string name="got_it" msgid="4428750913636945527">"حسنًا"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 47294c4..1ace3cd 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+    <string name="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 923ff79..6d3e0a9 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Güvənli məkanda saxlayın"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
@@ -43,10 +45,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bir əlli rejimdən istifadə edilir"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
-    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bir əlli rejimi başladın"</string>
-    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bir əlli rejimdən çıxın"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim başlasın"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Birəlli rejimdən çıxın"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+    <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 02e609cd..358da25 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+    <string name="got_it" msgid="4428750913636945527">"Važi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index ccea318..7a934cc 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Перайсці да наступнага"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перайсці да папярэдняга"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змяніць памер"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Схаваць"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+    <string name="got_it" msgid="4428750913636945527">"Зразумела"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index d29660b..02930b1 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Към следващия елемент"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Към предишния елемент"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Преоразмеряване"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Съхраняване"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+    <string name="got_it" msgid="4428750913636945527">"Разбрах"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 84bcaf9..b35e179 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"স্ট্যাস করুন"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+    <string name="got_it" msgid="4428750913636945527">"বুঝেছি"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 85e08d7..14d90a4 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavljanje u stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+    <string name="got_it" msgid="4428750913636945527">"Razumijem"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index a80b7fb..9cffbdd 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Amaga"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+    <string name="got_it" msgid="4428750913636945527">"Entesos"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e8257bc..9b5206a 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Uložit"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+    <string name="got_it" msgid="4428750913636945527">"Rozumím"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 17f8286..a06abf1 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skjul"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index f04796a..c5e79f8 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
@@ -60,13 +62,15 @@
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf „Verwalten“, um Bubbles für diese App zu deaktivieren"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index cc329e8..fc397c5 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Μετάβαση στο επόμενο"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Μετάβαση στο προηγούμενο"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Αλλαγή μεγέθους"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Απόκρυψη"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+    <string name="got_it" msgid="4428750913636945527">"Το κατάλαβα"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index d8b5b400..87210d5 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Skip to next‎‏‎‎‏‎"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎Skip to previous‎‏‎‎‏‎"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎Resize‎‏‎‎‏‎"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Stash‎‏‎‎‏‎"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎App may not work with split-screen.‎‏‎‎‏‎"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎App does not support split-screen.‎‏‎‎‏‎"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
+    <string name="got_it" msgid="4428750913636945527">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎Got it‎‏‎‎‏‎"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 7244b1a..ebe41e8 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Almacenar de manera segura"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
@@ -58,7 +60,7 @@
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
-    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+    <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 65e75bd..5949099 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
@@ -43,10 +45,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
-    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
-    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -60,7 +62,7 @@
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+    <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 0ccfcfe..8330981 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Pane hoidlasse"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+    <string name="got_it" msgid="4428750913636945527">"Selge"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6682ea8..6649769 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -25,9 +25,11 @@
     <string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
     <string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
     <string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
-    <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
-    <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+    <string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
+    <string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Gorde"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ados"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a41811d..f646039 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"رد شدن به بعدی"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"رد شدن به قبلی"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغییر اندازه"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"مخفی‌سازی"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
@@ -43,7 +45,7 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"تمام‌صفحه پایین"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از «حالت تک حرکت»"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از حالت یک‌دستی"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحه‌نمایش تند به‌طرف بالا بکشید یا در هر جایی از بالای برنامه که می‌خواهید ضربه بزنید"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت تک حرکت»"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت تک حرکت»"</string>
@@ -62,11 +64,13 @@
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابک‌ها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
+    <string name="got_it" msgid="4428750913636945527">"متوجه‌ام"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fcdc70f..5f87163 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Lisää turvasäilytykseen"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index ed82237..68df591 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index ad98b85..eecc9cb 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -61,7 +63,7 @@
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
-    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</string>
+    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôlez les bulles à tout moment"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 529825e..3583caf 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+    <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ee23e1e9..e0654bd 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"છુપાવો"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+    <string name="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 34c1c85..55a30f2 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"अगले पर जाएं"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"पिछले पर जाएं"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदलें"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"छिपाएं"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ऐप्लिकेशन शायद स्प्लिट स्क्रीन मोड में काम न करे."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्‍क्रीन का समर्थन नहीं करता है."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
@@ -67,6 +69,8 @@
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
-    <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+    <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+    <string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 32b21aa..f6acb5c 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sakrijte"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+    <string name="got_it" msgid="4428750913636945527">"Shvaćam"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 123b127..0c1c8a40 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Félretevés"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+    <string name="got_it" msgid="4428750913636945527">"Rendben"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b047cf1..36204c1 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Անցնել հաջորդին"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Վերադառնալ նախորդին"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Փոխել չափը"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Թաքցնել"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+    <string name="got_it" msgid="4428750913636945527">"Եղավ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index a75cdb4..de962c4 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+    <string name="got_it" msgid="4428750913636945527">"Oke"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 3b28148..c205d22 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Geymsla"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ég skil"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 8a2b9db..c788a03 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Accantona"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
@@ -60,7 +62,7 @@
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 20114a7..b0c03ed 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -18,16 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
-    <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+    <string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
     <string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
     <string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
     <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
-    <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+    <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
     <string name="pip_play" msgid="3496151081459417097">"הפעלה"</string>
-    <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+    <string name="pip_pause" msgid="690688849510295232">"השהיה"</string>
     <string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"הסתרה זמנית"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -41,14 +43,14 @@
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"עליון 30%"</string>
+    <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"למעלה 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"מסך תחתון מלא"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש במצב שימוש ביד אחת"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש בתכונה \'מצב שימוש ביד אחת\'"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"הפעלה של מצב שימוש ביד אחת"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"יציאה ממצב שימוש ביד אחת"</string>
-    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות בשביל בועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"גלישה"</string>
+    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות לבועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"אפשרויות נוספות"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"הוספה בחזרה לערימה"</string>
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
     <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+    <string name="got_it" msgid="4428750913636945527">"הבנתי"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0..ef98a9c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
-    <string name="pip_close" msgid="9135220303720555525">"‏סגור PIP"</string>
+    <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fbb2951..36700bd 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"次へスキップ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"前へスキップ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"サイズ変更"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"非表示"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
@@ -61,7 +63,7 @@
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
-    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"バブルはいつでも管理可能"</string>
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index f978481..af1377a 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"შემდეგზე გადასვლა"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"წინაზე გადასვლა"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ზომის შეცვლა"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"გადანახვა"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+    <string name="got_it" msgid="4428750913636945527">"გასაგებია"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 2d27faf..6deb0b8 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Келесіге өту"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Алдыңғысына оралу"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлшемін өзгерту"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Жасыру"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
@@ -68,5 +70,7 @@
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
-    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+    <string name="got_it" msgid="4428750913636945527">"Түсінікті"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d503b7a..c59d0fc 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"រំលងទៅបន្ទាប់"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"រំលងទៅក្រោយ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ប្ដូរ​ទំហំ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"លាក់ជាបណ្ដោះអាសន្ន"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធី​អាចនឹងមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ។"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
+    <string name="got_it" msgid="4428750913636945527">"យល់ហើយ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 3d61d84..5e655b4 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index ea7ad56..af34ef4 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"다음으로 건너뛰기"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"이전으로 건너뛰기"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"크기 조절"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"숨기기"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+    <string name="got_it" msgid="4428750913636945527">"확인"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 611b2d6..c16041d 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Мурункусуна өткөрүп жиберүү"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлчөмүн өзгөртүү"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сейфке салуу"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
@@ -62,11 +64,13 @@
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+    <string name="got_it" msgid="4428750913636945527">"Түшүндүм"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a1c998c..a578b0a 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ຂ້າມໄປລາຍການໜ້າ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ປ່ຽນຂະໜາດ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ເກັບໄວ້ບ່ອນເກັບສ່ວນຕົວ"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+    <string name="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b2ccd57..e037839 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslėpti"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+    <string name="got_it" msgid="4428750913636945527">"Supratau"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index e6d0c77..05472e4 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslēpt"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+    <string name="got_it" msgid="4428750913636945527">"Labi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 43f2881..9cb2c69 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Прескокни до следната"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Прескокни до претходната"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промени големина"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сокријте"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+    <string name="got_it" msgid="4428750913636945527">"Сфатив"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index e675861..f0bf513 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"സ്റ്റാഷ് ചെയ്യൽ"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
@@ -66,7 +68,9 @@
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
-    <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+    <string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
-    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+    <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 044fd9f..68822cb 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Дараагийн медиад очих"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Өмнөх медиад очих"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Хэмжээг өөрчлөх"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Нуух"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ойлголоо"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index e838cf5..a4b7be4 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्टॅश करा"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+    <string name="got_it" msgid="4428750913636945527">"समजले"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 6664f38..2f33bfa 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sembunyikan"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 9681d14..018ffc0 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"အရွယ်အစားပြောင်းရန်"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"သိုဝှက်ရန်"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ။"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+    <string name="got_it" msgid="4428750913636945527">"ရပြီ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 986e890..a23ad90 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Oppbevar"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+    <string name="got_it" msgid="4428750913636945527">"Greit"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 0369c6d..5b9b872 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -28,9 +28,11 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"अर्कोमा जानुहोस्"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"अघिल्लोमा जानुहोस्"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्ट्यास गर्नुहोस्"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
-    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
     <string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
     <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+    <string name="got_it" msgid="4428750913636945527">"बुझेँ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 26c276e..06aaad7 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -22,12 +22,14 @@
     <string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
     <string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
     <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
-    <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
+    <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en zet je de functie uit."</string>
     <string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
     <string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Verbergen"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
@@ -43,10 +45,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met één hand gebruiken"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
-    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met één hand starten"</string>
-    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met één hand afsluiten"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met 1 hand afsluiten"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string>
@@ -58,15 +60,17 @@
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
-    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+    <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te zetten"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 27f1622..ac1e84a 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ଲୁଚାନ୍ତୁ"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+    <string name="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 96688b9..bf5b733c 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ਆਕਾਰ ਬਦਲੋ"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ਸਟੈਸ਼"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+    <string name="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 6b640b5..cd659ba 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Przenieś do schowka"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 465d2d1..9c97ffe 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index df841bf..1f5b0ab 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Armazenar"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 465d2d1..9c97ffe 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 55a4376..d694be1 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stocați"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulați stocarea"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8ae00d2..e9bfffb 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти к следующему"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти к предыдущему"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Изменить размер"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Скрыть"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+    <string name="got_it" msgid="4428750913636945527">"ОК"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 081926f..ba178f0 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ඊළඟ එකට පනින්න"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"පෙර එකට පනින්න"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ප්‍රතිප්‍රමාණ කරන්න"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"සඟවා තබන්න"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්‍රියා නොකළ හැකිය"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+    <string name="got_it" msgid="4428750913636945527">"තේරුණා"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 24fded7..e048ca1 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skryť"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+    <string name="got_it" msgid="4428750913636945527">"Dobre"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 3f42530..ed05908 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Zakrij"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+    <string name="got_it" msgid="4428750913636945527">"Razumem"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index ddae724..13e830c 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Fshih"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+    <string name="got_it" msgid="4428750913636945527">"E kuptova"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 74c9ac0..be6857b 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ставите у тајну меморију"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+    <string name="got_it" msgid="4428750913636945527">"Важи"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 81328a8..e61e69b 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Utför stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 4559832..476af11 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ficha"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+    <string name="got_it" msgid="4428750913636945527">"Nimeelewa"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 586ee94..bc27389 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"அடுத்ததற்குச் செல்"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"முந்தையதற்குச் செல்"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"அளவு மாற்று"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"திரைப் பிரிப்பு அம்சத்தில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+    <string name="got_it" msgid="4428750913636945527">"சரி"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 4e85b43..624b8b3 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
+    <string name="got_it" msgid="4428750913636945527">"అర్థమైంది"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 66c7018..9017b3f 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"ข้ามไปรายการถัดไป"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"ข้ามไปรายการก่อนหน้า"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ปรับขนาด"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"เก็บเข้าที่เก็บส่วนตัว"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+    <string name="got_it" msgid="4428750913636945527">"รับทราบ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a76bf6f..c484caf 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"I-stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index b3276da..ca856a1 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Depola"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+    <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 8e303cf..08e8d29 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти далі"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти назад"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змінити розмір"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сховати"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Додаток може не працювати в режимі розділеного екрана."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+    <string name="got_it" msgid="4428750913636945527">"Зрозуміло"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 4b0adc6..06c0927 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+    <string name="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 74b135d..6a873a3 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Berkitish"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+    <string name="got_it" msgid="4428750913636945527">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index ce37231..4d4eebc 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ẩn"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+    <string name="got_it" msgid="4428750913636945527">"Tôi hiểu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 3143130..3b8c889 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一个"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一个"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"调整大小"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"隐藏"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+    <string name="got_it" msgid="4428750913636945527">"知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 4f8bfe0..9ba82b5 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"保護"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+    <string name="got_it" msgid="4428750913636945527">"知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 6fb8ed9..aa66653 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"暫時隱藏"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
@@ -44,7 +46,7 @@
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"以全螢幕顯示底部畫面"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"使用單手模式"</string>
-    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上的任何位置"</string>
+    <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"啟動單手模式"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"結束單手模式"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+    <string name="got_it" msgid="4428750913636945527">"我知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index cab2776..c8199c8 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -28,6 +28,8 @@
     <string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
     <string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+    <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Yenza isiteshi"</string>
+    <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
@@ -69,4 +71,6 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+    <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+    <string name="got_it" msgid="4428750913636945527">"Ngiyezwa"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 34c66a4..bf074b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -97,6 +97,14 @@
         b.setParent(sc);
     }
 
+    public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
+        final SurfaceControl sc = mLeashes.get(displayId);
+        if (sc == null) {
+            throw new IllegalArgumentException("can't find display" + displayId);
+        }
+        tx.setPosition(sc, x, y);
+    }
+
     @Override
     public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
             @NonNull SurfaceControl leash) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 0b941b5..9113c79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -103,6 +103,8 @@
                 return runMoveToSideStage(args, pw);
             case "removeFromSideStage":
                 return runRemoveFromSideStage(args, pw);
+            case "setSideStageOutline":
+                return runSetSideStageOutline(args, pw);
             case "setSideStagePosition":
                 return runSetSideStagePosition(args, pw);
             case "setSideStageVisibility":
@@ -161,6 +163,18 @@
         return true;
     }
 
+    private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
+        if (args.length < 3) {
+            // First arguments are "WMShell" and command name.
+            pw.println("Error: whether to enable or disable side stage outline border should be"
+                    + " provided as arguments");
+            return false;
+        }
+        final boolean enable = new Boolean(args[2]);
+        mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
+        return true;
+    }
+
     private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
         if (args.length < 3) {
             // First arguments are "WMShell" and command name.
@@ -175,7 +189,7 @@
     private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
         if (args.length < 3) {
             // First arguments are "WMShell" and command name.
-            pw.println("Error: side stage position should be provided as arguments");
+            pw.println("Error: side stage visibility should be provided as arguments");
             return false;
         }
         final Boolean visible = new Boolean(args[2]);
@@ -197,6 +211,8 @@
         pw.println("    Move a task with given id in split-screen mode.");
         pw.println("  removeFromSideStage <taskId>");
         pw.println("    Remove a task with given id in split-screen mode.");
+        pw.println("  setSideStageOutline <true/false>");
+        pw.println("    Enable/Disable outline on the side-stage.");
         pw.println("  setSideStagePosition <SideStagePosition>");
         pw.println("    Sets the position of the side-stage.");
         pw.println("  setSideStageVisibility <true/false>");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 1861e48..2f3214d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -40,6 +40,8 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.wm.shell.common.SyncTransactionQueue;
+
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
@@ -74,6 +76,7 @@
 
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Executor mShellExecutor;
+    private final SyncTransactionQueue mSyncQueue;
 
     private ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mTaskToken;
@@ -89,11 +92,12 @@
     private final Rect mTmpRootRect = new Rect();
     private final int[] mTmpLocation = new int[2];
 
-    public TaskView(Context context, ShellTaskOrganizer organizer) {
+    public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
         super(context, null, 0, 0, true /* disableBackgroundLayer */);
 
         mTaskOrganizer = organizer;
         mShellExecutor = organizer.getExecutor();
+        mSyncQueue = syncQueue;
         setUseAlpha();
         getHolder().addCallback(this);
         mGuard.open("release");
@@ -189,8 +193,7 @@
 
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setBounds(mTaskToken, mTmpRect);
-        // TODO(b/151449487): Enable synchronization
-        mTaskOrganizer.applyTransaction(wct);
+        mSyncQueue.queue(wct);
     }
 
     /**
@@ -236,14 +239,16 @@
     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) {
-            final int taskId = mTaskInfo.taskId;
+        mSyncQueue.queue(wct);
+        if (mListener == null) {
+            return;
+        }
+        int taskId = mTaskInfo.taskId;
+        mSyncQueue.runInSync((t) -> {
             mListenerExecutor.execute(() -> {
                 mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
             });
-        }
+        });
     }
 
     @Override
@@ -264,10 +269,12 @@
             updateTaskVisibility();
         }
         mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
-        // TODO: Synchronize show with the resize
         onLocationChanged();
         if (taskInfo.taskDescription != null) {
-            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+            int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
+            mSyncQueue.runInSync((t) -> {
+                setResizeBackgroundColor(t, backgroundColor);
+            });
         }
 
         if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 58ca1fb..8286d10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -30,12 +30,14 @@
 public class TaskViewFactoryController {
     private final ShellTaskOrganizer mTaskOrganizer;
     private final ShellExecutor mShellExecutor;
+    private final SyncTransactionQueue mSyncQueue;
     private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
 
     public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
-            ShellExecutor shellExecutor) {
+            ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
         mTaskOrganizer = taskOrganizer;
         mShellExecutor = shellExecutor;
+        mSyncQueue = syncQueue;
     }
 
     public TaskViewFactory asTaskViewFactory() {
@@ -44,7 +46,7 @@
 
     /** Creates an {@link TaskView} */
     public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
-        TaskView taskView = new TaskView(context, mTaskOrganizer);
+        TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
         executor.execute(() -> {
             onCreate.accept(taskView);
         });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index e6d088e..94a8758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -187,6 +187,7 @@
                     .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
                             mTaskInfo2.positionInParent.y)
                     .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+                    .show(dividerLeash)
                     .show(mRootTaskLeash)
                     .show(mTaskLeash1)
                     .show(mTaskLeash2);
@@ -212,9 +213,12 @@
             }
             mRootTaskInfo = taskInfo;
 
-            if (mSplitLayout != null
-                    && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
-                onBoundsChanged(mSplitLayout);
+            if (mSplitLayout != null) {
+                if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+                    onBoundsChanged(mSplitLayout);
+                }
+                // updateConfiguration re-inits the dividerbar, so show it now
+                mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
             }
         } else if (taskInfo.taskId == getTaskId1()) {
             mTaskInfo1 = taskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 09fcb86e5..28e2f7dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -85,6 +85,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -137,6 +138,7 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final DisplayController mDisplayController;
+    private final SyncTransactionQueue mSyncQueue;
 
     // Used to post to main UI thread
     private final ShellExecutor mMainExecutor;
@@ -209,7 +211,8 @@
             ShellTaskOrganizer organizer,
             DisplayController displayController,
             ShellExecutor mainExecutor,
-            Handler mainHandler) {
+            Handler mainHandler,
+            SyncTransactionQueue syncQueue) {
         BubbleLogger logger = new BubbleLogger(uiEventLogger);
         BubblePositioner positioner = new BubblePositioner(context, windowManager);
         BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
@@ -217,7 +220,7 @@
                 new BubbleDataRepository(context, launcherApps, mainExecutor),
                 statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                 logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
-                mainHandler);
+                mainHandler, syncQueue);
     }
 
     /**
@@ -239,7 +242,8 @@
             BubblePositioner positioner,
             DisplayController displayController,
             ShellExecutor mainExecutor,
-            Handler mainHandler) {
+            Handler mainHandler,
+            SyncTransactionQueue syncQueue) {
         mContext = context;
         mLauncherApps = launcherApps;
         mBarService = statusBarService == null
@@ -262,6 +266,7 @@
         mSavedBubbleKeysPerUser = new SparseSetArray<>();
         mBubbleIconFactory = new BubbleIconFactory(context);
         mDisplayController = displayController;
+        mSyncQueue = syncQueue;
     }
 
     public void initialize() {
@@ -561,6 +566,10 @@
         return mTaskOrganizer;
     }
 
+    SyncTransactionQueue getSyncTransactionQueue() {
+        return mSyncQueue;
+    }
+
     /** Contains information to help position things on the screen.  */
     BubblePositioner getPositioner() {
         return mBubblePositioner;
@@ -897,8 +906,7 @@
      * Fills the overflow bubbles by loading them from disk.
      */
     void loadOverflowBubblesFromDisk() {
-        if (!mBubbleData.getOverflowBubbles().isEmpty() && !mOverflowDataLoadNeeded) {
-            // we don't need to load overflow bubbles from disk if it is already in memory
+        if (!mOverflowDataLoadNeeded) {
             return;
         }
         mOverflowDataLoadNeeded = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 9687ec6..a02fa9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -25,6 +25,7 @@
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -60,7 +61,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
 import com.android.wm.shell.TaskView;
 import com.android.wm.shell.common.AlphaOptimizedButton;
@@ -77,7 +77,6 @@
 
     // The triangle pointing to the expanded view
     private View mPointerView;
-    private int mPointerMargin;
     @Nullable private int[] mExpandedViewContainerLocation;
 
     private AlphaOptimizedButton mManageButton;
@@ -102,9 +101,6 @@
      */
     private boolean mIsAlphaAnimating = false;
 
-    private int mMinHeight;
-    private int mOverflowHeight;
-    private int mManageButtonHeight;
     private int mPointerWidth;
     private int mPointerHeight;
     private float mPointerRadius;
@@ -338,7 +334,8 @@
             bringChildToFront(mOverflowView);
             mManageButton.setVisibility(GONE);
         } else {
-            mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+            mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+                    mController.getSyncTransactionQueue());
             mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
             mExpandedViewContainer.addView(mTaskView);
             bringChildToFront(mTaskView);
@@ -347,12 +344,8 @@
 
     void updateDimensions() {
         Resources res = getResources();
-        mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
-        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
-
         updateFontSize();
 
-        mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
         mPointerRadius = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_radius);
@@ -368,7 +361,6 @@
             updatePointerView();
         }
 
-        mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
         if (mManageButton != null) {
             int visibility = mManageButton.getVisibility();
             removeView(mManageButton);
@@ -632,12 +624,11 @@
         }
 
         if ((mBubble != null && mTaskView != null) || mIsOverflow) {
-            float desiredHeight = mIsOverflow
-                    ? mPositioner.isLargeScreen() ? getMaxExpandedHeight() : mOverflowHeight
-                    : mBubble.getDesiredHeight(mContext);
-            desiredHeight = Math.max(desiredHeight, mMinHeight);
-            float height = Math.min(desiredHeight, getMaxExpandedHeight());
-            height = Math.max(height, mMinHeight);
+            float desiredHeight = mPositioner.getExpandedViewHeight(mBubble);
+            int maxHeight = mPositioner.getMaxExpandedViewHeight(mIsOverflow);
+            float height = desiredHeight == MAX_HEIGHT
+                    ? maxHeight
+                    : Math.min(desiredHeight, maxHeight);
             FrameLayout.LayoutParams lp = mIsOverflow
                     ? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams()
                     : (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
@@ -661,23 +652,6 @@
         }
     }
 
-    private int getMaxExpandedHeight() {
-        int expandedContainerY = mExpandedViewContainerLocation != null
-                // Remove top insets back here because availableRect.height would account for that
-                ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top
-                : 0;
-        int settingsHeight = mIsOverflow ? 0 : mManageButtonHeight;
-        int pointerHeight = mPositioner.showBubblesVertically()
-                ? mPointerWidth
-                : (int) (mPointerHeight - mPointerOverlap + mPointerMargin);
-        return mPositioner.getAvailableRect().height()
-                - expandedContainerY
-                - getPaddingTop()
-                - getPaddingBottom()
-                - settingsHeight
-                - pointerHeight;
-    }
-
     /**
      * Update appearance of the expanded view being displayed.
      *
@@ -727,14 +701,11 @@
                 : mPointerHeight - mPointerOverlap;
         setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);
 
-        final float expandedViewY = mPositioner.getExpandedViewY();
-        // TODO: I don't understand why it works but it does - why normalized in portrait
-        //  & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
-        final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
-                mPositioner.getBubbleSize());
-        final float bubbleCenter = showVertically
-                ? bubblePosition + (mPositioner.getBubbleSize() / 2f) - expandedViewY
-                : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+        // Subtract the expandedViewY here because the pointer is placed within the expandedView.
+        float pointerPosition = mPositioner.getPointerPosition(bubblePosition);
+        final float bubbleCenter = mPositioner.showBubblesVertically()
+                ? pointerPosition - mPositioner.getExpandedViewY(mBubble, bubblePosition)
+                : pointerPosition;
         // Post because we need the width of the view
         post(() -> {
             float pointerY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c600f56..0a856a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -34,6 +34,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
 
 import java.lang.annotation.Retention;
@@ -58,6 +59,8 @@
 
     /** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
     public static final int NUM_VISIBLE_WHEN_RESTING = 2;
+    /** Indicates a bubble's height should be the maximum available space. **/
+    public static final int MAX_HEIGHT = -1;
 
     private Context mContext;
     private WindowManager mWindowManager;
@@ -68,13 +71,16 @@
     private int mMaxBubbles;
 
     private int mBubbleSize;
-    private int mBubbleBadgeSize;
     private int mSpacingBetweenBubbles;
     private int mExpandedViewLargeScreenWidth;
     private int mExpandedViewPadding;
     private int mPointerMargin;
-    private float mPointerWidth;
-    private float mPointerHeight;
+    private int mPointerWidth;
+    private int mPointerHeight;
+    private int mPointerOverlap;
+    private int mManageButtonHeight;
+    private int mExpandedViewMinHeight;
+    private int mOverflowHeight;
 
     private PointF mPinLocation;
     private PointF mRestingStackPosition;
@@ -151,7 +157,6 @@
 
         Resources res = mContext.getResources();
         mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
-        mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size);
         mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
         mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
 
@@ -161,6 +166,10 @@
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
         mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
+        mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
+        mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
+        mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
+        mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
 
         mMaxBubbles = calculateMaxBubbles();
 
@@ -296,8 +305,8 @@
         return mPaddings;
     }
 
-    /** Calculates the y position of the expanded view when it is expanded. */
-    public float getExpandedViewY() {
+    /** Gets the y position of the expanded view if it was top-aligned. */
+    private float getExpandedViewYTopAligned() {
         final int top = getAvailableRect().top;
         if (showBubblesVertically()) {
             return top - mPointerWidth;
@@ -306,6 +315,116 @@
         }
     }
 
+    /** The maximum height the expanded view can be. */
+    public int getMaxExpandedViewHeight(boolean isOverflow) {
+        int paddingTop = showBubblesVertically()
+                ? 0
+                : mPointerHeight;
+        int settingsHeight = isOverflow ? 0 : mManageButtonHeight;
+        // Subtract pointer size because it's laid out in LinearLayout with the expanded view.
+        int pointerSize = showBubblesVertically()
+                ? mPointerWidth
+                : (mPointerHeight + mPointerMargin);
+        // Subtract top insets because availableRect.height would account for that
+        int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+        return getAvailableRect().height()
+                - expandedContainerY
+                - paddingTop
+                - settingsHeight
+                - pointerSize;
+    }
+
+    /**
+     * Determines the height for the bubble, ensuring a minimum height. If the height should be as
+     * big as available, returns {@link #MAX_HEIGHT}.
+     */
+    public float getExpandedViewHeight(BubbleViewProvider bubble) {
+        boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+        float desiredHeight = isOverflow
+                ? mOverflowHeight
+                : ((Bubble) bubble).getDesiredHeight(mContext);
+        int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
+        desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight);
+        if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) {
+            return MAX_HEIGHT;
+        }
+        return desiredHeight;
+    }
+
+    /**
+     * Gets the y position for the expanded view. This is the position on screen of the top
+     * horizontal line of the expanded view.
+     *
+     * @param bubble the bubble being positioned.
+     * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+     *                       bubble if showing vertically.
+     * @return the y position for the expanded view.
+     */
+    public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
+        boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+        float expandedViewHeight = getExpandedViewHeight(bubble);
+        float topAlignment = getExpandedViewYTopAligned();
+        if (!showBubblesVertically() || expandedViewHeight == MAX_HEIGHT) {
+            // Top-align when bubbles are shown at the top or are max size.
+            return topAlignment;
+        }
+        // If we're here, we're showing vertically & developer has made height less than maximum.
+        int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
+        float pointerPosition = getPointerPosition(bubblePosition);
+        float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
+        float topIfCentered = pointerPosition - (expandedViewHeight / 2);
+        if (topIfCentered > mPositionRect.top && mPositionRect.bottom > bottomIfCentered) {
+            // Center it
+            return pointerPosition - mPointerWidth - (expandedViewHeight / 2f);
+        } else if (topIfCentered <= mPositionRect.top) {
+            // Top align
+            return topAlignment;
+        } else {
+            // Bottom align
+            return mPositionRect.bottom - manageButtonHeight - expandedViewHeight - mPointerWidth;
+        }
+    }
+
+    /**
+     * The position the pointer points to, the center of the bubble.
+     *
+     * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+     *                       bubble if showing vertically.
+     * @return the position the tip of the pointer points to. The x position if showing on top, the
+     * y position if showing vertically.
+     */
+    public float getPointerPosition(float bubblePosition) {
+        // TODO: I don't understand why it works but it does - why normalized in portrait
+        //  & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
+        final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
+                getBubbleSize());
+        return showBubblesVertically()
+                ? bubblePosition + (getBubbleSize() / 2f)
+                : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+    }
+
+    /**
+     * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
+     * row. When in landscape or on a large screen, 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 the row.
+     * @param numberOfBubbles the number of bubbles (including the overflow) in the row.
+     * @return the y position of the bubble if showing vertically and the x position if showing
+     * horizontally.
+     */
+    public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) {
+        final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles);
+        final float expandedStackSize = (numberOfBubbles * mBubbleSize)
+                + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+        final float centerPosition = showBubblesVertically()
+                ? mPositionRect.centerY()
+                : mPositionRect.centerX();
+        final float rowStart = centerPosition - (expandedStackSize / 2f);
+        return rowStart + positionInBar;
+    }
+
     /**
      * Sets the stack's most recent position along the edge of the screen. This is saved when the
      * last bubble is removed, so that the stack can be restored in its previous position.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 92e455c..d460195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -574,20 +574,17 @@
                 mBubbleContainer.setActiveController(mStackAnimationController);
                 hideFlyoutImmediate();
 
-                if (!mPositioner.showingInTaskbar()) {
-                    // Also, save the magnetized stack so we can dispatch touch events to it.
-                    mMagnetizedObject = mStackAnimationController.getMagnetizedStack(
-                            mMagneticTarget);
-                    mMagnetizedObject.setMagnetListener(mStackMagnetListener);
-                } else {
+                if (mPositioner.showingInTaskbar()) {
                     // In taskbar, the stack isn't draggable so we shouldn't dispatch touch events.
                     mMagnetizedObject = null;
+                } else {
+                    // Save the magnetized stack so we can dispatch touch events to it.
+                    mMagnetizedObject = mStackAnimationController.getMagnetizedStack();
+                    mMagnetizedObject.clearAllTargets();
+                    mMagnetizedObject.addTarget(mMagneticTarget);
+                    mMagnetizedObject.setMagnetListener(mStackMagnetListener);
                 }
 
-                // Also, save the magnetized stack so we can dispatch touch events to it.
-                mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
-                mMagnetizedObject.setMagnetListener(mStackMagnetListener);
-
                 mIsDraggingStack = true;
 
                 // Cancel animations to make the stack temporarily invisible, since we're now
@@ -865,7 +862,6 @@
                         mRelativeStackPositionBeforeRotation = null;
                     }
 
-                    setUpDismissView();
                     if (mIsExpanded) {
                         // Re-draw bubble row and pointer for new orientation.
                         beforeExpandedViewAnimation();
@@ -874,8 +870,10 @@
                         mExpandedAnimationController.expandFromStack(() -> {
                             afterExpandedViewAnimation();
                         } /* after */);
+                        final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+                                getBubbleIndex(mExpandedBubble));
                         mExpandedViewContainer.setTranslationX(0f);
-                        mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+                        mExpandedViewContainer.setTranslationY(translationY);
                         mExpandedViewContainer.setAlpha(1f);
                     }
                     removeOnLayoutChangeListener(mOrientationChangedListener);
@@ -1027,10 +1025,9 @@
                 contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
 
         // Save the MagneticTarget instance for the newly set up view - we'll add this to the
-        // MagnetizedObjects.
+        // MagnetizedObjects when the dismiss view gets shown.
         mMagneticTarget = new MagnetizedObject.MagneticTarget(
                 mDismissView.getCircle(), dismissRadius);
-
         mBubbleContainer.bringToFront();
     }
 
@@ -1524,6 +1521,7 @@
                     bubble.cleanupViews();
                 }
                 updatePointerPosition();
+                updateExpandedView();
                 logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
             }
@@ -1815,9 +1813,10 @@
             mTaskbarScrim.setVisibility(VISIBLE);
             mTaskbarScrim.animate().alpha(1f).start();
         }
-
+        final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+                getBubbleIndex(mExpandedBubble));
         mExpandedViewContainer.setTranslationX(0f);
-        mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+        mExpandedViewContainer.setTranslationY(translationY);
         mExpandedViewContainer.setAlpha(1f);
 
         int index;
@@ -1866,7 +1865,7 @@
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     bubbleWillBeAt + mBubbleSize / 2f,
-                    mPositioner.getExpandedViewY());
+                    translationY);
         }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
@@ -1970,7 +1969,7 @@
             mExpandedViewContainerMatrix.setScale(
                     1f, 1f,
                     expandingFromBubbleAt + mBubbleSize / 2f,
-                    mPositioner.getExpandedViewY());
+                    mPositioner.getExpandedViewY(mExpandedBubble, index));
         }
 
         mExpandedViewAlphaAnimator.reverse();
@@ -2077,7 +2076,7 @@
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     expandingFromBubbleDestination + mBubbleSize / 2f,
-                    mPositioner.getExpandedViewY());
+                    mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination));
         }
 
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -2698,7 +2697,9 @@
             mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         }
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-            mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+            mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
+                    mExpandedAnimationController.getBubbleXOrYForOrientation(
+                            getBubbleIndex(mExpandedBubble))));
             mExpandedViewContainer.setTranslationX(0f);
             mExpandedBubble.getExpandedView().updateView(
                     mExpandedViewContainer.getLocationOnScreen());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index df2b440..efe07fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -83,12 +83,6 @@
     private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
-    /** Max number of bubbles shown in row above expanded view. */
-    private int mBubblesMaxRendered;
-    /** Max amount of space to have between bubbles when expanded. */
-    private int mBubblesMaxSpace;
-    /** Amount of space between the bubbles when expanded. */
-    private float mSpaceBetweenBubbles;
     /** Whether the expand / collapse animation is running. */
     private boolean mAnimatingExpand = false;
 
@@ -211,8 +205,6 @@
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubbleSizePx = mPositioner.getBubbleSize();
-        mBubblesMaxRendered = mPositioner.getMaxBubbles();
-        mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
     }
 
     /**
@@ -628,14 +620,13 @@
         }
     }
 
-    // TODO - could move to method on bubblePositioner if mSpaceBetweenBubbles gets moved
     /**
      * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
      * row. When in landscape or on a large screen, 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.
+     * @param index bubble index in the row.
      * @return the y position of the bubble if showing vertically and the x position if showing
      * horizontally.
      */
@@ -643,15 +634,6 @@
         if (mLayout == null) {
             return 0;
         }
-        final float positionInBar = index * (mBubbleSizePx + mSpaceBetweenBubbles);
-        Rect availableRect = mPositioner.getAvailableRect();
-        final boolean isLandscape = mPositioner.showBubblesVertically();
-        final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
-                + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
-        final float centerPosition = isLandscape
-                ? availableRect.centerY()
-                : availableRect.centerX();
-        final float rowStart = centerPosition - (expandedStackSize / 2f);
-        return rowStart + positionInBar;
+        return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount());
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 0802fb5..636e145 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -1024,11 +1024,9 @@
     }
 
     /**
-     * Returns the {@link MagnetizedObject} instance for the bubble stack, with the provided
-     * {@link MagnetizedObject.MagneticTarget} added as a target.
+     * Returns the {@link MagnetizedObject} instance for the bubble stack.
      */
-    public MagnetizedObject<StackAnimationController> getMagnetizedStack(
-            MagnetizedObject.MagneticTarget target) {
+    public MagnetizedObject<StackAnimationController> getMagnetizedStack() {
         if (mMagnetizedStack == null) {
             mMagnetizedStack = new MagnetizedObject<StackAnimationController>(
                     mLayout.getContext(),
@@ -1053,7 +1051,6 @@
                     loc[1] = (int) mStackPosition.y;
                 }
             };
-            mMagnetizedStack.addTarget(target);
             mMagnetizedStack.setHapticsEnabled(true);
             mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
         }
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 a7996f05..6f63369 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
@@ -279,9 +279,9 @@
                     if (!mImeShowing) {
                         removeImeSurface();
                     }
-                }
-                if (mImeSourceControl != null) {
-                    mImeSourceControl.release(SurfaceControl::release);
+                    if (mImeSourceControl != null) {
+                        mImeSourceControl.release(SurfaceControl::release);
+                    }
                 }
                 mImeSourceControl = imeSourceControl;
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 33beab5..f3a8620 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -18,13 +18,15 @@
 
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
-import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.transition.LegacyTransitions;
 
 import java.util.ArrayList;
 
@@ -77,6 +79,21 @@
     }
 
     /**
+     * Queues a legacy transition to be sent serially to WM
+     */
+    public void queue(LegacyTransitions.ILegacyTransition transition,
+            @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
+        SyncCallback cb = new SyncCallback(transition, type, wct);
+        synchronized (mQueue) {
+            if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
+            mQueue.add(cb);
+            if (mQueue.size() == 1) {
+                cb.send();
+            }
+        }
+    }
+
+    /**
      * Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
      * Otherwise just returns without queueing.
      * @return {@code true} if queued, {@code false} if not.
@@ -118,12 +135,12 @@
     // Synchronized on mQueue
     private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
         if (DEBUG) Slog.d(TAG, "  Running " + mRunnables.size() + " sync runnables");
-        for (int i = 0, n = mRunnables.size(); i < n; ++i) {
+        final int n = mRunnables.size();
+        for (int i = 0; i < n; ++i) {
             mRunnables.get(i).runWithTransaction(t);
         }
-        mRunnables.clear();
-        t.apply();
-        t.close();
+        // More runnables may have been added, so only remove the ones that ran.
+        mRunnables.subList(0, n).clear();
     }
 
     /** Task to run with transaction. */
@@ -135,20 +152,38 @@
     private class SyncCallback extends WindowContainerTransactionCallback {
         int mId = -1;
         final WindowContainerTransaction mWCT;
+        final LegacyTransitions.LegacyTransition mLegacyTransition;
 
         SyncCallback(WindowContainerTransaction wct) {
             mWCT = wct;
+            mLegacyTransition = null;
+        }
+
+        SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
+                @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
+            mWCT = wct;
+            mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
         }
 
         // Must be sychronized on mQueue
         void send() {
+            if (mInFlight == this) {
+                // This was probably queued up and sent during a sync runnable of the last callback.
+                // Don't queue it again.
+                return;
+            }
             if (mInFlight != null) {
                 throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
                         + mInFlight.mId + " - " + mInFlight.mWCT);
             }
             mInFlight = this;
             if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
-            mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+            if (mLegacyTransition != null) {
+                mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
+                        mLegacyTransition.getAdapter(), this, mWCT);
+            } else {
+                mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+            }
             if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
             mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
         }
@@ -169,6 +204,16 @@
                     if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
                     mQueue.remove(this);
                     onTransactionReceived(t);
+                    if (mLegacyTransition != null) {
+                        try {
+                            mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
+                        }
+                    } else {
+                        t.apply();
+                        t.close();
+                    }
                     if (!mQueue.isEmpty()) {
                         mQueue.get(0).send();
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index 9f6dd1f..9e01259 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -303,6 +303,13 @@
     }
 
     /**
+     * Removes all associated targets from this object.
+     */
+    fun clearAllTargets() {
+        associatedTargets.clear()
+    }
+
+    /**
      * Provide this method with all motion events that move the magnetized object. If the
      * location of the motion events moves within the magnetic field of a target, or indicate a
      * fling-to-target gesture, this method will return true and you should not move the object
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 5b158d2..8adfac0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -92,12 +92,16 @@
     private DividerSnapAlgorithm mDividerSnapAlgorithm;
     private int mDividePosition;
     private boolean mInitialized = false;
+    private int mOrientation;
+    private int mRotation;
 
     public SplitLayout(String windowName, Context context, Configuration configuration,
             SplitLayoutHandler splitLayoutHandler,
             SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
             DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
         mContext = context.createConfigurationContext(configuration);
+        mOrientation = configuration.orientation;
+        mRotation = configuration.windowConfiguration.getRotation();
         mSplitLayoutHandler = splitLayoutHandler;
         mDisplayImeController = displayImeController;
         mSplitWindowManager = new SplitWindowManager(
@@ -144,25 +148,36 @@
 
     /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
     public boolean updateConfiguration(Configuration configuration) {
+        boolean affectsLayout = false;
+
+        // Make sure to render the divider bar with proper resources that matching the screen
+        // orientation.
+        final int orientation = configuration.orientation;
+        if (orientation != mOrientation) {
+            mOrientation = orientation;
+            mContext = mContext.createConfigurationContext(configuration);
+            mSplitWindowManager.setConfiguration(configuration);
+            affectsLayout = true;
+        }
+
+        // Update the split bounds when necessary. Besides root bounds changed, split bounds need to
+        // be updated when the rotation changed to cover the case that users rotated the screen 180
+        // degrees.
+        final int rotation = configuration.windowConfiguration.getRotation();
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
-        if (mRootBounds.equals(rootBounds)) {
-            return false;
+        if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+            mRootBounds.set(rootBounds);
+            mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+            resetDividerPosition();
+            affectsLayout = true;
         }
 
-        mContext = mContext.createConfigurationContext(configuration);
-        mSplitWindowManager.setConfiguration(configuration);
-        mRootBounds.set(rootBounds);
-        mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
-        resetDividerPosition();
-
-        // Don't inflate divider bar if it is not initialized.
-        if (!mInitialized) {
-            return false;
+        if (mInitialized) {
+            release();
+            init();
         }
 
-        release();
-        init();
-        return true;
+        return affectsLayout;
     }
 
     /** Updates recording bounds of divider window and both of the splits. */
@@ -271,7 +286,11 @@
     }
 
     private void flingDividePosition(int from, int to) {
-        if (from == to) return;
+        if (from == to) {
+            // No animation run, it should stop resizing here.
+            mSplitWindowManager.setResizingSplits(false);
+            return;
+        }
         ValueAnimator animator = ValueAnimator
                 .ofInt(from, to)
                 .setDuration(250);
@@ -296,9 +315,8 @@
         return context.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics()
                 .getWindowInsets()
-                .getInsets(WindowInsets.Type.navigationBars()
-                        | WindowInsets.Type.statusBars()
-                        | WindowInsets.Type.displayCutout()).toRect();
+                .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
+                .toRect();
     }
 
     private static boolean isLandscape(Rect bounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 0cea0ef..b7bbe80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -95,7 +95,7 @@
         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
                 .setContainerLayer()
                 .setName(TAG)
-                .setHidden(false)
+                .setHidden(true)
                 .setCallsite("SplitWindowManager#attachToParentSurface");
         mParentContainerCallbacks.attachToParentSurface(builder);
         mLeash = builder.build();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index 362b40f..9bed40d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -460,6 +460,7 @@
     private void stopDragging() {
         mHandle.setTouching(false, true /* animate */);
         mWindowManager.setSlippery(true);
+        mWindowManagerProxy.setResizing(false);
         releaseBackground();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index d9409ec..b1fa2ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -204,7 +204,8 @@
 
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (transition != mPendingDismiss && transition != mPendingEnter) {
             // If we're not in split-mode, just abort
@@ -239,12 +240,12 @@
                 if (change.getParent() != null) {
                     // This is probably reparented, so we want the parent to be immediately visible
                     final TransitionInfo.Change parentChange = info.getChange(change.getParent());
-                    t.show(parentChange.getLeash());
-                    t.setAlpha(parentChange.getLeash(), 1.f);
+                    startTransaction.show(parentChange.getLeash());
+                    startTransaction.setAlpha(parentChange.getLeash(), 1.f);
                     // and then animate this layer outside the parent (since, for example, this is
                     // the home task animating from fullscreen to part-screen).
-                    t.reparent(leash, info.getRootLeash());
-                    t.setLayer(leash, info.getChanges().size() - i);
+                    startTransaction.reparent(leash, info.getRootLeash());
+                    startTransaction.setLayer(leash, info.getChanges().size() - i);
                     // build the finish reparent/reposition
                     mFinishTransaction.reparent(leash, parentChange.getLeash());
                     mFinishTransaction.setPosition(leash,
@@ -271,12 +272,12 @@
             if (transition == mPendingEnter
                     && mListener.mPrimary.token.equals(change.getContainer())
                     || mListener.mSecondary.token.equals(change.getContainer())) {
-                t.setWindowCrop(leash, change.getStartAbsBounds().width(),
+                startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(),
                         change.getStartAbsBounds().height());
                 if (mListener.mPrimary.token.equals(change.getContainer())) {
                     // Move layer to top since we want it above the oversized home task during
                     // animation even though home task is on top in hierarchy.
-                    t.setLayer(leash, info.getChanges().size() + 1);
+                    startTransaction.setLayer(leash, info.getChanges().size() + 1);
                 }
             }
             boolean isOpening = Transitions.isOpeningType(info.getType());
@@ -289,7 +290,7 @@
                     // Dismissing via snap-to-top/bottom means that the dismissed task is already
                     // not-visible (usually cropped to oblivion) so immediately set its alpha to 0
                     // and don't animate it so it doesn't pop-in when reparented.
-                    t.setAlpha(leash, 0.f);
+                    startTransaction.setAlpha(leash, 0.f);
                 } else {
                     startExampleAnimation(leash, false /* show */);
                 }
@@ -311,7 +312,7 @@
             }
             mSplitScreen.finishEnterSplitTransition(homeIsVisible);
         }
-        t.apply();
+        startTransaction.apply();
         onFinish();
         return true;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 5911f8d..aa6961a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -169,9 +169,22 @@
      * @return true if user enabled one-handed shortcut in settings, false otherwise.
      */
     public boolean getShortcutEnabled(ContentResolver resolver, int userId) {
-        final String targets = Settings.Secure.getStringForUser(resolver,
+        // Checks SOFTWARE_SHORTCUT_KEY
+        final String targetsSwKey = Settings.Secure.getStringForUser(resolver,
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
-        return TextUtils.isEmpty(targets) ? false : targets.contains(ONE_HANDED_MODE_TARGET_NAME);
+        if (!TextUtils.isEmpty(targetsSwKey) && targetsSwKey.contains(
+                ONE_HANDED_MODE_TARGET_NAME)) {
+            return true;
+        }
+
+        // Checks HARDWARE_SHORTCUT_KEY
+        final String targetsHwKey = Settings.Secure.getStringForUser(resolver,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+        if (!TextUtils.isEmpty(targetsHwKey) && targetsHwKey.contains(
+                ONE_HANDED_MODE_TARGET_NAME)) {
+            return true;
+        }
+        return false;
     }
 
     /**
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 5a50619..a0d83c0 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
@@ -114,38 +114,6 @@
      */
     private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
 
-    // Not a complete set of states but serves what we want right now.
-    private enum State {
-        UNDEFINED(0),
-        TASK_APPEARED(1),
-        ENTRY_SCHEDULED(2),
-        ENTERING_PIP(3),
-        ENTERED_PIP(4),
-        EXITING_PIP(5);
-
-        private final int mStateValue;
-
-        State(int value) {
-            mStateValue = value;
-        }
-
-        private boolean isInPip() {
-            return mStateValue >= TASK_APPEARED.mStateValue
-                    && mStateValue != EXITING_PIP.mStateValue;
-        }
-
-        /**
-         * Resize request can be initiated in other component, ignore if we are no longer in PIP,
-         * still waiting for animation or we're exiting from it.
-         *
-         * @return {@code true} if the resize request should be blocked/ignored.
-         */
-        private boolean shouldBlockResizeRequest() {
-            return mStateValue < ENTERING_PIP.mStateValue
-                    || mStateValue == EXITING_PIP.mStateValue;
-        }
-    }
-
     private final Context mContext;
     private final SyncTransactionQueue mSyncTransactionQueue;
     private final PipBoundsState mPipBoundsState;
@@ -201,7 +169,8 @@
             }
             final boolean isExitPipDirection = isOutPipDirection(direction)
                     || isRemovePipDirection(direction);
-            if (mState != State.EXITING_PIP || isExitPipDirection) {
+            if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
+                    || isExitPipDirection) {
                 // Finish resize as long as we're not exiting PIP, or, if we are, only if this is
                 // the end of an exit PIP animation.
                 // This is necessary in case there was a resize animation ongoing when exit PIP
@@ -244,7 +213,7 @@
     private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
-    private State mState = State.UNDEFINED;
+    private PipTransitionState mPipTransitionState;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private long mLastOneShotAlphaAnimationTime;
     private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -289,6 +258,7 @@
 
     public PipTaskOrganizer(Context context,
             @NonNull SyncTransactionQueue syncTransactionQueue,
+            @NonNull PipTransitionState pipTransitionState,
             @NonNull PipBoundsState pipBoundsState,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
@@ -302,6 +272,7 @@
             @ShellMainThread ShellExecutor mainExecutor) {
         mContext = context;
         mSyncTransactionQueue = syncTransactionQueue;
+        mPipTransitionState = pipTransitionState;
         mPipBoundsState = pipBoundsState;
         mPipBoundsAlgorithm = boundsHandler;
         mPipMenuController = pipMenuController;
@@ -337,14 +308,14 @@
     }
 
     public boolean isInPip() {
-        return mState.isInPip();
+        return mPipTransitionState.isInPip();
     }
 
     /**
      * Returns whether the entry animation is waiting to be started.
      */
     public boolean isEntryScheduled() {
-        return mState == State.ENTRY_SCHEDULED;
+        return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED;
     }
 
     /**
@@ -412,9 +383,11 @@
      * @param animationDurationMs duration in millisecond for the exiting PiP transition
      */
     public void exitPip(int animationDurationMs) {
-        if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
+        if (!mPipTransitionState.isInPip()
+                || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
+                || mToken == null) {
             Log.wtf(TAG, "Not allowed to exitPip in current state"
-                    + " mState=" + mState + " mToken=" + mToken);
+                    + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
             return;
         }
 
@@ -438,7 +411,12 @@
         wct.setBoundsChangeTransaction(mToken, tx);
         // Set the exiting state first so if there is fixed rotation later, the running animation
         // won't be interrupted by alpha animation for existing PiP.
-        mState = State.EXITING_PIP;
+        mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mPipTransitionController.startTransition(destinationBounds, wct);
+            return;
+        }
         mSyncTransactionQueue.queue(wct);
         mSyncTransactionQueue.runInSync(t -> {
             // Make sure to grab the latest source hint rect as it could have been
@@ -476,9 +454,9 @@
      * Removes PiP immediately.
      */
     public void removePip() {
-        if (!mState.isInPip() ||  mToken == null) {
+        if (!mPipTransitionState.isInPip() ||  mToken == null) {
             Log.wtf(TAG, "Not allowed to removePip in current state"
-                    + " mState=" + mState + " mToken=" + mToken);
+                    + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
             return;
         }
 
@@ -492,10 +470,19 @@
         animator.setDuration(mExitAnimationDuration);
         animator.setInterpolator(Interpolators.ALPHA_OUT);
         animator.start();
-        mState = State.EXITING_PIP;
+        mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
     }
 
     private void removePipImmediately() {
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setBounds(mToken, null);
+            wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+            wct.reorder(mToken, false);
+            mPipTransitionController.startTransition(null, wct);
+            return;
+        }
+
         try {
             // Reset the task bounds first to ensure the activity configuration is reset as well
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -514,7 +501,7 @@
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
         mTaskInfo = info;
         mToken = mTaskInfo.token;
-        mState = State.TASK_APPEARED;
+        mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED);
         mLeash = leash;
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
         setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
@@ -557,6 +544,8 @@
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
                 mPipMenuController.attach(mLeash);
+            } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+                mOneShotAnimationType = ANIM_TYPE_BOUNDS;
             }
             return;
         }
@@ -568,7 +557,7 @@
             scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
                     sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
                     null /* updateBoundsCallback */);
-            mState = State.ENTERING_PIP;
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -595,7 +584,7 @@
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         animateResizePip(currentBounds, destinationBounds, sourceHintRect,
                 TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */);
-        mState = State.ENTERING_PIP;
+        mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
     }
 
     /**
@@ -620,7 +609,7 @@
                 mSurfaceControlTransactionFactory.getTransaction();
         tx.setAlpha(mLeash, 0f);
         tx.apply();
-        mState = State.ENTRY_SCHEDULED;
+        mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
         applyEnterPipSyncTransaction(destinationBounds, () -> {
             mPipAnimationController
                     .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
@@ -631,11 +620,17 @@
                     .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;
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         }, null /* boundsChangeTransaction */);
     }
 
     private void onEndOfSwipePipToHomeTransition() {
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mInSwipePipToHomeTransition = false;
+            mSwipePipToHomeOverlay = null;
+            return;
+        }
+
         final Rect destinationBounds = mPipBoundsState.getBounds();
         final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -679,7 +674,7 @@
     private void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            mState = State.ENTERING_PIP;
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         }
         mPipTransitionController.sendOnPipTransitionStarted(direction);
     }
@@ -688,7 +683,7 @@
     void sendOnPipTransitionFinished(
             @PipAnimationController.TransitionDirection int direction) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            mState = State.ENTERED_PIP;
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
         }
         mPipTransitionController.sendOnPipTransitionFinished(direction);
         // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
@@ -713,7 +708,7 @@
      */
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
-        if (mState == State.UNDEFINED) {
+        if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
             return;
         }
         final WindowContainerToken token = info.token;
@@ -725,7 +720,7 @@
         clearWaitForFixedRotation();
         mInSwipePipToHomeTransition = false;
         mPictureInPictureParams = null;
-        mState = State.UNDEFINED;
+        mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
         // Re-set the PIP bounds to none.
         mPipBoundsState.setBounds(new Rect());
         mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -750,8 +745,10 @@
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
-        if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) {
-            Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+        if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP
+                && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) {
+            Log.d(TAG, "Defer onTaskInfoChange in current state: "
+                    + mPipTransitionState.getTransitionState());
             // Defer applying PiP parameters if the task is entering PiP to avoid disturbing
             // the animation.
             mDeferredTaskInfo = info;
@@ -784,7 +781,7 @@
         mNextRotation = newRotation;
         mWaitForFixedRotation = true;
 
-        if (mState.isInPip()) {
+        if (mPipTransitionState.isInPip()) {
             // Fade out the existing PiP to avoid jump cut during seamless rotation.
             fadeExistingPip(false /* show */);
         }
@@ -795,7 +792,7 @@
         if (!mWaitForFixedRotation) {
             return;
         }
-        if (mState == State.TASK_APPEARED) {
+        if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
             if (mInSwipePipToHomeTransition) {
                 onEndOfSwipePipToHomeTransition();
             } else {
@@ -803,9 +800,11 @@
                 enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(),
                         mEnterAnimationDuration);
             }
-        } else if (mState == State.ENTERED_PIP && mHasFadeOut) {
+        } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP
+                && mHasFadeOut) {
             fadeExistingPip(true /* show */);
-        } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) {
+        } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP
+                && mDeferredAnimEndTransaction != null) {
             final PipAnimationController.PipTransitionAnimator<?> animator =
                     mPipAnimationController.getCurrentAnimator();
             final Rect destinationBounds = animator.getDestinationBounds();
@@ -859,13 +858,13 @@
         // note that this can be called when swipe-to-home or fixed-rotation is happening.
         // Skip this entirely if that's the case.
         final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation
-                && (mState != State.ENTERED_PIP);
+                && (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP);
         if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) {
             if (DEBUG) {
                 Log.d(TAG, "Skip onMovementBoundsChanged on rotation change"
                         + " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition
                         + " mWaitForFixedRotation=" + mWaitForFixedRotation
-                        + " mState=" + mState);
+                        + " getTransitionState=" + mPipTransitionState.getTransitionState());
             }
             return;
         }
@@ -873,7 +872,7 @@
                 mPipAnimationController.getCurrentAnimator();
         if (animator == null || !animator.isRunning()
                 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
-            final boolean rotatingPip = mState.isInPip() && fromRotation;
+            final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
             if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
                 // The position will be used by fade-in animation when the fixed rotation is done.
                 mPipBoundsState.setBounds(destinationBoundsOut);
@@ -1006,7 +1005,7 @@
             Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect,
             @PipAnimationController.TransitionDirection int direction, int durationMs,
             Consumer<Rect> updateBoundsCallback) {
-        if (!mState.isInPip()) {
+        if (!mPipTransitionState.isInPip()) {
             // TODO: tend to use shouldBlockResizeRequest here as well but need to consider
             // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
             // container transaction callback and we want to set the mState immediately.
@@ -1036,7 +1035,7 @@
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, toBounds)
-                .round(tx, mLeash, mState.isInPip());
+                .round(tx, mLeash, mPipTransitionState.isInPip());
         if (mPipMenuController.isMenuVisible()) {
             mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
         } else {
@@ -1114,7 +1113,7 @@
     public void scheduleFinishResizePip(Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
             Consumer<Rect> updateBoundsCallback) {
-        if (mState.shouldBlockResizeRequest()) {
+        if (mPipTransitionState.shouldBlockResizeRequest()) {
             return;
         }
 
@@ -1131,7 +1130,7 @@
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
                 .resetScale(tx, mLeash, destinationBounds)
-                .round(tx, mLeash, mState.isInPip());
+                .round(tx, mLeash, mPipTransitionState.isInPip());
         return tx;
     }
 
@@ -1140,7 +1139,7 @@
      */
     public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
             Consumer<Rect> updateBoundsCallback) {
-        if (mState.shouldBlockResizeRequest()) {
+        if (mPipTransitionState.shouldBlockResizeRequest()) {
             return;
         }
         if (mWaitForFixedRotation) {
@@ -1419,7 +1418,7 @@
         pw.println(innerPrefix + "mToken=" + mToken
                 + " binder=" + (mToken != null ? mToken.asBinder() : null));
         pw.println(innerPrefix + "mLeash=" + mLeash);
-        pw.println(innerPrefix + "mState=" + mState);
+        pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 4759550..18153e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_PIP;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -25,9 +27,12 @@
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 
 import android.app.TaskInfo;
 import android.content.Context;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.Surface;
@@ -35,6 +40,7 @@
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -49,64 +55,185 @@
  */
 public class PipTransition extends PipTransitionController {
 
+    private final PipTransitionState mPipTransitionState;
     private final int mEnterExitAnimationDuration;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private Transitions.TransitionFinishCallback mFinishCallback;
+    private Rect mExitDestinationBounds = new Rect();
 
     public PipTransition(Context context,
-            PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+            PipBoundsState pipBoundsState,
+            PipTransitionState pipTransitionState,
+            PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
             Transitions transitions,
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
         super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
                 pipAnimationController, transitions, shellTaskOrganizer);
+        mPipTransitionState = pipTransitionState;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
     }
 
     @Override
+    public void setIsFullAnimation(boolean isFullAnimation) {
+        setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA);
+    }
+
+    /**
+     * Sets the preferred animation type for one time.
+     * This is typically used to set the animation type to
+     * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
+     */
+    private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+        mOneShotAnimationType = animationType;
+    }
+
+    @Override
+    public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+        if (destinationBounds != null) {
+            mExitDestinationBounds.set(destinationBounds);
+            mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+        } else {
+            mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
+        }
+    }
+
+    @Override
     public boolean startAnimation(@android.annotation.NonNull IBinder transition,
             @android.annotation.NonNull TransitionInfo info,
-            @android.annotation.NonNull SurfaceControl.Transaction t,
+            @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
+            @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
             @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+        if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
+            final TransitionInfo.Change change = info.getChanges().get(0);
+            mFinishCallback = finishCallback;
+            startTransaction.apply();
+            boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
+                    new Rect(mExitDestinationBounds));
+            mExitDestinationBounds.setEmpty();
+            return success;
+        }
+
+        if (info.getType() == TRANSIT_REMOVE_PIP) {
+            startTransaction.apply();
+            finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+                    mPipBoundsState.getDisplayBounds());
+            finishCallback.onTransitionFinished(null, null);
+            return true;
+        }
+
+        // Search for an Enter PiP transition (along with a show wallpaper one)
+        TransitionInfo.Change enterPip = null;
+        TransitionInfo.Change wallpaper = null;
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
             if (change.getTaskInfo() != null
                     && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
                     == WINDOWING_MODE_PINNED) {
-                mFinishCallback = finishCallback;
-                return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+                enterPip = change;
+            } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+                wallpaper = change;
             }
         }
-        return false;
+        if (enterPip == null) {
+            return false;
+        }
+
+        // Show the wallpaper if there is a wallpaper change.
+        if (wallpaper != null) {
+            startTransaction.show(wallpaper.getLeash());
+            startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+        }
+
+        mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+        mFinishCallback = finishCallback;
+        return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+                startTransaction, finishTransaction);
     }
 
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
-        return null;
+        if (request.getType() == TRANSIT_PIP) {
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
+            final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+            wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED);
+            wct.setBounds(request.getTriggerTask().token, destinationBounds);
+            return wct;
+        } else {
+            return null;
+        }
     }
 
     @Override
     public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
             SurfaceControl.Transaction tx) {
+
+        if (isInPipDirection(direction)) {
+            mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+        }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareFinishResizeTransaction(taskInfo, destinationBounds,
                 direction, tx, wct);
-        mFinishCallback.onTransitionFinished(wct, null);
+        mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() {
+            @Override
+            public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) {
+                t.merge(tx);
+                t.apply();
+            }
+        });
         finishResizeForMenu(destinationBounds);
     }
 
+    private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+            final Rect destinationBounds) {
+        PipAnimationController.PipTransitionAnimator animator =
+                mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
+                        mPipBoundsState.getBounds(), destinationBounds, null,
+                        TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+
+        animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+                .setPipAnimationCallback(mPipAnimationCallback)
+                .setDuration(mEnterExitAnimationDuration)
+                .start();
+
+        return true;
+    }
+
     private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
-            final SurfaceControl.Transaction t) {
+            final SurfaceControl.Transaction startTransaction,
+            final SurfaceControl.Transaction finishTransaction) {
         setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
                 taskInfo.topActivityInfo);
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
         PipAnimationController.PipTransitionAnimator animator;
+        finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
+        if (taskInfo.pictureInPictureParams != null
+                && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+            mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+            // PiP menu is attached late in the process here to avoid any artifacts on the leash
+            // caused by addShellRoot when in gesture navigation mode.
+            mPipMenuController.attach(leash);
+            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+            tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
+                    .setPosition(leash, destinationBounds.left, destinationBounds.top)
+                    .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
+            startTransaction.merge(tx);
+            startTransaction.apply();
+            mPipBoundsState.setBounds(destinationBounds);
+            onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx);
+            sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+            mFinishCallback = null;
+            return true;
+        }
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             final Rect sourceHintRect =
                     PipBoundsAlgorithm.getValidSourceHintRect(
@@ -115,8 +242,10 @@
                     currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
                     0 /* startingAngle */, Surface.ROTATION_0);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
-            t.setAlpha(leash, 0f);
-            t.apply();
+            startTransaction.setAlpha(leash, 0f);
+            // PiP menu is attached late in the process here to avoid any artifacts on the leash
+            // caused by addShellRoot when in gesture navigation mode.
+            mPipMenuController.attach(leash);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
                     0f, 1f);
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -124,6 +253,7 @@
             throw new RuntimeException("Unrecognized animation type: "
                     + mOneShotAnimationType);
         }
+        startTransaction.apply();
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
@@ -158,6 +288,5 @@
         }
 
         wct.setBounds(taskInfo.token, taskBounds);
-        wct.setBoundsChangeTransaction(taskInfo.token, tx);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d801c91..dedc566 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.transition.Transitions;
@@ -46,6 +47,7 @@
     protected final PipBoundsState mPipBoundsState;
     protected final ShellTaskOrganizer mShellTaskOrganizer;
     protected final PipMenuController mPipMenuController;
+    protected final Transitions mTransitions;
     private final Handler mMainHandler;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
 
@@ -98,6 +100,22 @@
             SurfaceControl.Transaction tx) {
     }
 
+    /**
+     * Called to inform the transition that the animation should start with the assumption that
+     * PiP is not animating from its original bounds, but rather a continuation of another
+     * animation. For example, gesture navigation would first fade out the PiP activity, and the
+     * transition should be responsible to animate in (such as fade in) the PiP.
+     */
+    public void setIsFullAnimation(boolean isFullAnimation) {
+    }
+
+    /**
+     * Called when the Shell wants to starts a transition/animation.
+     */
+    public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+        // Default implementation does nothing.
+    }
+
     public PipTransitionController(PipBoundsState pipBoundsState,
             PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController, Transitions transitions,
@@ -107,6 +125,7 @@
         mShellTaskOrganizer = shellTaskOrganizer;
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipAnimationController = pipAnimationController;
+        mTransitions = transitions;
         mMainHandler = new Handler(Looper.getMainLooper());
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             transitions.addHandler(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
new file mode 100644
index 0000000..d23aada
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
+ * {@link PipTransition}.
+ */
+public class PipTransitionState {
+
+    public static final int UNDEFINED = 0;
+    public static final int TASK_APPEARED = 1;
+    public static final int ENTRY_SCHEDULED = 2;
+    public static final int ENTERING_PIP = 3;
+    public static final int ENTERED_PIP = 4;
+    public static final int EXITING_PIP = 5;
+
+    // Not a complete set of states but serves what we want right now.
+    @IntDef(prefix = { "TRANSITION_STATE_" }, value =  {
+            UNDEFINED,
+            TASK_APPEARED,
+            ENTRY_SCHEDULED,
+            ENTERING_PIP,
+            ENTERED_PIP,
+            EXITING_PIP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransitionState {}
+
+    private @TransitionState int mState;
+
+    public PipTransitionState() {
+        mState = UNDEFINED;
+    }
+
+    public void setTransitionState(@TransitionState int state) {
+        mState = state;
+    }
+
+    public @TransitionState int getTransitionState() {
+        return mState;
+    }
+
+    public boolean isInPip() {
+        return mState >= TASK_APPEARED
+                && mState != EXITING_PIP;
+    }
+
+    /**
+     * Resize request can be initiated in other component, ignore if we are no longer in PIP,
+     * still waiting for animation or we're exiting from it.
+     *
+     * @return {@code true} if the resize request should be blocked/ignored.
+     */
+    public boolean shouldBlockResizeRequest() {
+        return mState < ENTERING_PIP
+                || mState == EXITING_PIP;
+    }
+}
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 4f3ec96..62b50c5 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
@@ -67,6 +67,7 @@
 import com.android.wm.shell.pip.IPipAnimationListener;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
@@ -528,6 +529,8 @@
 
     private void setPinnedStackAnimationType(int animationType) {
         mPipTaskOrganizer.setOneShotAnimationType(animationType);
+        mPipTransitionController.setIsFullAnimation(
+                animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
     }
 
     private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index b7caf72..551476d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -58,7 +58,8 @@
 
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         return false;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 8f0892f..df1f5e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -20,6 +20,8 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
 import android.window.IRemoteTransition;
 
 import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -77,9 +79,22 @@
             int position, in Bundle options) = 9;
 
     /**
-     * Starts tasks simultaneously in one transition. The first task in the list will be in the
-     * main-stage and on the left/top.
+     * Starts tasks simultaneously in one transition.
      */
     oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
             in Bundle sideOptions, int sidePosition, in IRemoteTransition remoteTransition) = 10;
+
+    /**
+     * Version of startTasks using legacy transition system.
+     */
+     oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+                            int sideTaskId, in Bundle sideOptions, int sidePosition,
+                            in RemoteAnimationAdapter adapter) = 11;
+
+    /**
+     * Blocking call that notifies and gets additional split-screen targets when entering
+     * recents (for example: the dividerBar).
+     * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
+     */
+    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) = 12;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
new file mode 100644
index 0000000..0b763f2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
+ * the consideration of display insets like status bar, navigation bar and display cutout.
+ */
+class OutlineManager extends WindowlessWindowManager {
+    private static final String WINDOW_NAME = "SplitOutlineLayer";
+    private final Context mContext;
+    private final Rect mOutlineBounds = new Rect();
+    private final Rect mTmpBounds = new Rect();
+    private SurfaceControlViewHost mViewHost;
+    private SurfaceControl mHostLeash;
+    private SurfaceControl mLeash;
+    private int mOutlineColor;
+
+    OutlineManager(Context context, Configuration configuration) {
+        super(configuration, null /* rootSurface */, null /* hostInputToken */);
+        mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+                null /* options */);
+    }
+
+    @Override
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+        b.setParent(mHostLeash);
+    }
+
+    boolean drawOutlineBounds(Rect rootBounds) {
+        if (mLeash == null || mViewHost == null) return false;
+
+        computeOutlineBounds(mContext, rootBounds, mTmpBounds);
+        if (mOutlineBounds.equals(mTmpBounds)) {
+            return false;
+        }
+        mOutlineBounds.set(mTmpBounds);
+
+        ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
+        final WindowManager.LayoutParams lp =
+                (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+        lp.width = rootBounds.width();
+        lp.height = rootBounds.height();
+        mViewHost.relayout(lp);
+
+        return true;
+    }
+
+    void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
+        if (mLeash != null || mViewHost != null) return;
+
+        mHostLeash = hostLeash;
+        mOutlineColor = color;
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
+                .inflate(R.layout.split_outline, null);
+
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+        lp.token = new Binder();
+        lp.setTitle(WINDOW_NAME);
+        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+        // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+        //  TRUSTED_OVERLAY for windowless window without input channel.
+        mViewHost.setView(rootView, lp);
+        mLeash = getSurfaceControl(mViewHost.getWindowToken());
+        t.setLayer(mLeash, Integer.MAX_VALUE);
+    }
+
+    void release() {
+        if (mViewHost != null) {
+            mViewHost.release();
+        }
+    }
+
+    private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
+        computeDisplayStableBounds(context, outBounds);
+        outBounds.intersect(rootBounds);
+        // Offset the coordinate from screen based to surface based.
+        outBounds.offset(-rootBounds.left, -rootBounds.top);
+    }
+
+    private static void computeDisplayStableBounds(Context context, Rect outBounds) {
+        final WindowMetrics windowMetrics =
+                context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
+        outBounds.set(windowMetrics.getBounds());
+        outBounds.inset(windowMetrics.getWindowInsets().getInsets(
+                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
new file mode 100644
index 0000000..71d48ee
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.splitscreen;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/** Root layout for holding split outline. */
+public class OutlineRoot extends FrameLayout {
+    public OutlineRoot(@NonNull Context context) {
+        super(context);
+    }
+
+    public OutlineRoot(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private OutlineView mOutlineView;
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mOutlineView = findViewById(R.id.split_outline);
+    }
+
+    void updateOutlineBounds(Rect bounds, int color) {
+        mOutlineView.updateOutlineBounds(bounds, color);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
new file mode 100644
index 0000000..ea66180
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.splitscreen;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.R;
+
+/** View for drawing split outline. */
+public class OutlineView extends View {
+    private final Paint mPaint = new Paint();
+    private final Rect mBounds = new Rect();
+
+    public OutlineView(@NonNull Context context) {
+        super(context);
+    }
+
+    public OutlineView(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(getResources()
+                .getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
+    }
+
+    void updateOutlineBounds(Rect bounds, int color) {
+        if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
+        mBounds.set(bounds);
+        mPaint.setColor(color);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBounds.isEmpty()) return;
+        final Path path = new Region(mBounds).getBoundaryPath();
+        canvas.drawPath(path, mPaint);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 82f95a4..2b19bb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,7 +16,10 @@
 
 package com.android.wm.shell.splitscreen;
 
+import android.annotation.CallSuper;
 import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.view.SurfaceSession;
 import android.window.WindowContainerToken;
@@ -28,15 +31,19 @@
 /**
  * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
  * here. All other task are launch in the {@link MainStage}.
+ *
  * @see StageCoordinator
  */
 class SideStage extends StageTaskListener {
     private static final String TAG = SideStage.class.getSimpleName();
+    private final Context mContext;
+    private OutlineManager mOutlineManager;
 
-    SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
+    SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
             StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
             SurfaceSession surfaceSession) {
         super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+        mContext = context;
     }
 
     void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
@@ -44,7 +51,7 @@
         final WindowContainerToken rootToken = mRootTaskInfo.token;
         wct.setBounds(rootToken, rootBounds)
                 .reparent(task.token, rootToken, true /* onTop*/)
-                // Moving the root task to top after the child tasks were repareted , or the root
+                // Moving the root task to top after the child tasks were reparented , or the root
                 // task cannot be visible and focused.
                 .reorder(rootToken, true /* onTop */);
     }
@@ -69,4 +76,34 @@
         wct.reparent(task.token, newParent, false /* onTop */);
         return true;
     }
+
+    void enableOutline(boolean enable) {
+        if (enable) {
+            if (mOutlineManager == null && mRootTaskInfo != null) {
+                mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration);
+                mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW));
+                updateOutlineBounds();
+            }
+        } else {
+            if (mOutlineManager != null) {
+                mOutlineManager.release();
+                mOutlineManager = null;
+            }
+        }
+    }
+
+    private void updateOutlineBounds() {
+        if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
+        mOutlineManager.drawOutlineBounds(
+                mRootTaskInfo.configuration.windowConfiguration.getBounds());
+    }
+
+    @Override
+    @CallSuper
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        super.onTaskInfoChanged(taskInfo);
+        if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
+            updateOutlineBounds();
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 002bfb6..d6afeba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,6 +64,12 @@
         return null;
     }
 
+    /**
+     * Called when the keyguard occluded state changes.
+     * @param occluded Indicates if the keyguard is now occluded.
+     */
+    void onKeyguardOccludedChanged(boolean occluded);
+
     /** Get a string representation of a stage type */
     static String stageTypeToString(@StageType int stage) {
         switch (stage) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 9a457b5..aca545b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -17,14 +17,12 @@
 package com.android.wm.shell.splitscreen;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -39,7 +37,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.window.IRemoteTransition;
+import android.window.WindowContainerTransaction;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
@@ -56,6 +60,7 @@
 import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import com.android.wm.shell.transition.LegacyTransitions;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
@@ -140,8 +145,12 @@
         return mStageCoordinator.removeFromSideStage(taskId);
     }
 
+    public void setSideStageOutline(boolean enable) {
+        mStageCoordinator.setSideStageOutline(enable);
+    }
+
     public void setSideStagePosition(@SplitPosition int sideStagePosition) {
-        mStageCoordinator.setSideStagePosition(sideStagePosition);
+        mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
     }
 
     public void setSideStageVisibility(boolean visible) {
@@ -157,6 +166,10 @@
         mStageCoordinator.exitSplitScreen();
     }
 
+    public void onKeyguardOccludedChanged(boolean occluded) {
+        mStageCoordinator.onKeyguardOccludedChanged(occluded);
+    }
+
     public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
         mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
     }
@@ -175,7 +188,7 @@
 
     public void startTask(int taskId, @SplitScreen.StageType int stage,
             @SplitPosition int position, @Nullable Bundle options) {
-        options = resolveStartStage(stage, position, options);
+        options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
 
         try {
             ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
@@ -187,7 +200,7 @@
     public void startShortcut(String packageName, String shortcutId,
             @SplitScreen.StageType int stage, @SplitPosition int position,
             @Nullable Bundle options, UserHandle user) {
-        options = resolveStartStage(stage, position, options);
+        options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
 
         try {
             LauncherApps launcherApps =
@@ -202,64 +215,66 @@
     public void startIntent(PendingIntent intent, Intent fillInIntent,
             @SplitScreen.StageType int stage, @SplitPosition int position,
             @Nullable Bundle options) {
-        options = resolveStartStage(stage, position, options);
-
-        try {
-            intent.send(mContext, 0, fillInIntent, null, null, null, options);
-        } catch (PendingIntent.CanceledException e) {
-            Slog.e(TAG, "Failed to launch activity", e);
+        if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+            startIntentLegacy(intent, fillInIntent, stage, position, options);
+            return;
         }
+        mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+                null /* remote */);
     }
 
-    private Bundle resolveStartStage(@SplitScreen.StageType int stage,
-            @SplitPosition int position, @Nullable Bundle options) {
-        switch (stage) {
-            case STAGE_TYPE_UNDEFINED: {
-                // Use the stage of the specified position is valid.
-                if (position != SPLIT_POSITION_UNDEFINED) {
-                    if (position == mStageCoordinator.getSideStagePosition()) {
-                        options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
-                    } else {
-                        options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
-                    }
-                } else {
-                    // Exit split-screen and launch fullscreen since stage wasn't specified.
-                    mStageCoordinator.exitSplitScreen();
-                }
-                break;
-            }
-            case STAGE_TYPE_SIDE: {
-                if (position != SPLIT_POSITION_UNDEFINED) {
-                    mStageCoordinator.setSideStagePosition(position);
-                } else {
-                    position = mStageCoordinator.getSideStagePosition();
-                }
-                if (options == null) {
-                    options = new Bundle();
-                }
-                mStageCoordinator.updateActivityOptions(options, position);
-                break;
-            }
-            case STAGE_TYPE_MAIN: {
-                if (position != SPLIT_POSITION_UNDEFINED) {
-                    // Set the side stage opposite of what we want to the main stage.
-                    final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
-                            ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
-                    mStageCoordinator.setSideStagePosition(sideStagePosition);
-                } else {
-                    position = mStageCoordinator.getMainStagePosition();
-                }
-                if (options == null) {
-                    options = new Bundle();
-                }
-                mStageCoordinator.updateActivityOptions(options, position);
-                break;
-            }
-            default:
-                throw new IllegalArgumentException("Unknown stage=" + stage);
-        }
+    private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+            @SplitScreen.StageType int stage, @SplitPosition int position,
+            @Nullable Bundle options) {
+        final boolean wasInSplit = isSplitScreenVisible();
 
-        return options;
+        LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+            @Override
+            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+                    IRemoteAnimationFinishedCallback finishedCallback,
+                    SurfaceControl.Transaction t) {
+                boolean cancelled = apps == null || apps.length == 0;
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                if (cancelled) {
+                    if (!wasInSplit) {
+                        final WindowContainerTransaction undoWct = new WindowContainerTransaction();
+                        mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, undoWct);
+                        mSyncQueue.queue(undoWct);
+                        mSyncQueue.runInSync(undoT -> {
+                            // looks weird, but we want undoT to execute after t but still want the
+                            // rest of the syncQueue runnables to aggregate.
+                            t.merge(undoT);
+                            undoT.merge(t);
+                        });
+                        return;
+                    }
+                }
+                for (int i = 0; i < apps.length; ++i) {
+                    if (apps[i].mode == MODE_OPENING) {
+                        t.show(apps[i].leash);
+                    }
+                }
+                RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget();
+                t.show(divider.leash);
+                t.apply();
+                if (cancelled) return;
+                try {
+                    finishedCallback.onAnimationFinished();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error finishing legacy transition: ", e);
+                }
+            }
+        };
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+        wct.sendPendingIntent(intent, fillInIntent, options);
+        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+    }
+
+    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+        if (!isSplitScreenVisible()) return null;
+        return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
     }
 
     public void dump(@NonNull PrintWriter pw, String prefix) {
@@ -284,6 +299,13 @@
             mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
             return mISplitScreen;
         }
+
+        @Override
+        public void onKeyguardOccludedChanged(boolean occluded) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.onKeyguardOccludedChanged(occluded);
+            });
+        }
     }
 
     /**
@@ -417,6 +439,16 @@
         }
 
         @Override
+        public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+                int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+                RemoteAnimationAdapter adapter) {
+            executeRemoteCallWithTaskPermission(mController, "startTasks",
+                    (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
+                            mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
+                            adapter));
+        }
+
+        @Override
         public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
                 int sideTaskId, @Nullable Bundle sideOptions,
                 @SplitPosition int sidePosition,
@@ -444,5 +476,14 @@
                         controller.startIntent(intent, fillInIntent, stage, position, options);
                     });
         }
+
+        @Override
+        public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+            final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
+            executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
+                    (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel),
+                    true /* blocking */);
+            return out[0];
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index c37789e..69d0be6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -84,17 +84,19 @@
     }
 
     void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
         mFinishCallback = finishCallback;
         mAnimatingTransition = transition;
         if (mRemoteHandler != null) {
-            mRemoteHandler.startAnimation(transition, info, t, mRemoteFinishCB);
+            mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
+                    mRemoteFinishCB);
             mRemoteHandler = null;
             return;
         }
-        playInternalAnimation(transition, info, t, mainRoot, sideRoot);
+        playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
     }
 
     private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0264c5a..81ce2b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -35,6 +36,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
 import static com.android.wm.shell.transition.Transitions.isClosingType;
 import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -42,11 +44,23 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.WindowConfiguration;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
@@ -115,7 +129,8 @@
     private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
     private final DisplayImeController mDisplayImeController;
     private final SplitScreenTransitions mSplitTransitions;
-    private boolean mExitSplitScreenOnHide = true;
+    private boolean mExitSplitScreenOnHide;
+    private boolean mKeyguardOccluded;
 
     // TODO(b/187041611): remove this flag after totally deprecated legacy split
     /** Whether the device is supporting legacy split or not. */
@@ -150,6 +165,7 @@
                 mSyncQueue,
                 mSurfaceSession);
         mSideStage = new SideStage(
+                mContext,
                 mTaskOrganizer,
                 mDisplayId,
                 mSideStageListener,
@@ -157,6 +173,10 @@
                 mSurfaceSession);
         mDisplayImeController = displayImeController;
         mRootTDAOrganizer.registerListener(displayId, this);
+        final DeviceStateManager deviceStateManager =
+                mContext.getSystemService(DeviceStateManager.class);
+        deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
+                new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
         mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                 mOnTransitionAnimationComplete);
         transitions.addHandler(this);
@@ -194,7 +214,7 @@
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitPosition int sideStagePosition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        setSideStagePosition(sideStagePosition);
+        setSideStagePosition(sideStagePosition, wct);
         mMainStage.activate(getMainStageBounds(), wct);
         mSideStage.addTask(task, getSideStageBounds(), wct);
         mTaskOrganizer.applyTransaction(wct);
@@ -215,6 +235,10 @@
         return result;
     }
 
+    void setSideStageOutline(boolean enable) {
+        mSideStage.enableOutline(enable);
+    }
+
     /** Starts 2 tasks in one transition. */
     void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
             @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@@ -222,7 +246,7 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mainOptions = mainOptions != null ? mainOptions : new Bundle();
         sideOptions = sideOptions != null ? sideOptions : new Bundle();
-        setSideStagePosition(sidePosition);
+        setSideStagePosition(sidePosition, wct);
 
         // Build a request WCT that will launch both apps such that task 0 is on the main stage
         // while task 1 is on the side stage.
@@ -241,6 +265,138 @@
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
     }
 
+    /** Starts 2 tasks in one legacy transition. */
+    void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+            int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+            RemoteAnimationAdapter adapter) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        // Need to add another wrapper here in shell so that we can inject the divider bar
+        // and also manage the process elevation via setRunningRemote
+        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+            @Override
+            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
+                    final IRemoteAnimationFinishedCallback finishedCallback) {
+                RemoteAnimationTarget[] augmentedNonApps =
+                        new RemoteAnimationTarget[nonApps.length + 1];
+                for (int i = 0; i < nonApps.length; ++i) {
+                    augmentedNonApps[i] = nonApps[i];
+                }
+                augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+                try {
+                    ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+                            adapter.getCallingApplication());
+                    adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
+                            finishedCallback);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error starting remote animation", e);
+                }
+            }
+
+            @Override
+            public void onAnimationCancelled() {
+                try {
+                    adapter.getRunner().onAnimationCancelled();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error starting remote animation", e);
+                }
+            }
+        };
+        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+                wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+        if (mainOptions == null) {
+            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+        } else {
+            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+        }
+
+        sideOptions = sideOptions != null ? sideOptions : new Bundle();
+        setSideStagePosition(sidePosition, wct);
+
+        // Build a request WCT that will launch both apps such that task 0 is on the main stage
+        // while task 1 is on the side stage.
+        mMainStage.activate(getMainStageBounds(), wct);
+        mSideStage.setBounds(getSideStageBounds(), wct);
+
+        // Make sure the launch options will put tasks in the corresponding split roots
+        addActivityOptions(mainOptions, mMainStage);
+        addActivityOptions(sideOptions, mSideStage);
+
+        // Add task launch requests
+        wct.startTask(mainTaskId, mainOptions);
+        wct.startTask(sideTaskId, sideOptions);
+
+        // Using legacy transitions, so we can't use blast sync since it conflicts.
+        mTaskOrganizer.applyTransaction(wct);
+    }
+
+    public void startIntent(PendingIntent intent, Intent fillInIntent,
+            @SplitScreen.StageType int stage, @SplitPosition int position,
+            @androidx.annotation.Nullable Bundle options,
+            @Nullable IRemoteTransition remoteTransition) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        options = resolveStartStage(stage, position, options, wct);
+        wct.sendPendingIntent(intent, fillInIntent, options);
+        mSplitTransitions.startEnterTransition(
+                TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
+    }
+
+    Bundle resolveStartStage(@SplitScreen.StageType int stage,
+            @SplitPosition int position, @androidx.annotation.Nullable Bundle options,
+            @androidx.annotation.Nullable WindowContainerTransaction wct) {
+        switch (stage) {
+            case STAGE_TYPE_UNDEFINED: {
+                // Use the stage of the specified position is valid.
+                if (position != SPLIT_POSITION_UNDEFINED) {
+                    if (position == getSideStagePosition()) {
+                        options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
+                    } else {
+                        options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
+                    }
+                } else {
+                    // Exit split-screen and launch fullscreen since stage wasn't specified.
+                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+                }
+                break;
+            }
+            case STAGE_TYPE_SIDE: {
+                if (position != SPLIT_POSITION_UNDEFINED) {
+                    setSideStagePosition(position, wct);
+                } else {
+                    position = getSideStagePosition();
+                }
+                if (options == null) {
+                    options = new Bundle();
+                }
+                updateActivityOptions(options, position, wct);
+                break;
+            }
+            case STAGE_TYPE_MAIN: {
+                if (position != SPLIT_POSITION_UNDEFINED) {
+                    // Set the side stage opposite of what we want to the main stage.
+                    final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
+                            ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
+                    setSideStagePosition(sideStagePosition, wct);
+                } else {
+                    position = getMainStagePosition();
+                }
+                if (options == null) {
+                    options = new Bundle();
+                }
+                updateActivityOptions(options, position, wct);
+                break;
+            }
+            default:
+                throw new IllegalArgumentException("Unknown stage=" + stage);
+        }
+
+        return options;
+    }
+
     @SplitLayout.SplitPosition
     int getSideStagePosition() {
         return mSideStagePosition;
@@ -252,18 +408,24 @@
                 ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
     }
 
-    void setSideStagePosition(@SplitPosition int sideStagePosition) {
-        setSideStagePosition(sideStagePosition, true /* updateVisibility */);
+    void setSideStagePosition(@SplitPosition int sideStagePosition,
+            @Nullable WindowContainerTransaction wct) {
+        setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
     }
 
     private void setSideStagePosition(@SplitPosition int sideStagePosition,
-            boolean updateVisibility) {
+            boolean updateBounds, @Nullable WindowContainerTransaction wct) {
         if (mSideStagePosition == sideStagePosition) return;
         mSideStagePosition = sideStagePosition;
         sendOnStagePositionChanged();
 
-        if (mSideStageListener.mVisible && updateVisibility) {
-            onStageVisibilityChanged(mSideStageListener);
+        if (mSideStageListener.mVisible && updateBounds) {
+            if (wct == null) {
+                // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds.
+                onBoundsChanged(mSplitLayout);
+            } else {
+                updateWindowBounds(mSplitLayout, wct);
+            }
         }
     }
 
@@ -275,6 +437,12 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
+    void onKeyguardOccludedChanged(boolean occluded) {
+        // Do not exit split directly, because it needs to wait for task info update to determine
+        // which task should remain on top after split dismissed.
+        mKeyguardOccluded = occluded;
+    }
+
     void exitSplitScreen() {
         exitSplitScreen(null /* childrenToTop */);
     }
@@ -292,7 +460,12 @@
         mSplitLayout.resetDividerPosition();
     }
 
-    private void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
+    /**
+     * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
+     * an existing WindowContainerTransaction (rather than applying immediately). This is intended
+     * to be used when exiting split might be bundled with other window operations.
+     */
+    void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
             @NonNull WindowContainerTransaction wct) {
         mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
         mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
@@ -307,15 +480,21 @@
         opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
     }
 
-    void updateActivityOptions(Bundle opts, @SplitPosition int position) {
+    void updateActivityOptions(Bundle opts, @SplitPosition int position,
+            @Nullable WindowContainerTransaction wct) {
         addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
 
         if (!mMainStage.isActive()) {
             // Activate the main stage in anticipation of an app launch.
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            boolean needsApply = wct == null;
+            if (needsApply) {
+                wct = new WindowContainerTransaction();
+            }
             mMainStage.activate(getMainStageBounds(), wct);
             mSideStage.setBounds(getSideStageBounds(), wct);
-            mTaskOrganizer.applyTransaction(wct);
+            if (needsApply) {
+                mTaskOrganizer.applyTransaction(wct);
+            }
         }
     }
 
@@ -403,22 +582,37 @@
         // Divider is only visible if both the main stage and side stages are visible
         setDividerVisibility(isSplitScreenVisible());
 
-        if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
-            // Exit split-screen if both stage are not visible.
-            // TODO: This is only a temporary request from UX and is likely to be removed soon...
-            exitSplitScreen();
+        if (!mainStageVisible && !sideStageVisible) {
+            if (mExitSplitScreenOnHide
+            // Don't dismiss staged split when both stages are not visible due to sleeping display,
+            // like the cases keyguard showing or screen off.
+            || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+                exitSplitScreen();
+            }
+        } else if (mKeyguardOccluded) {
+            // At least one of the stages is visible while keyguard occluded. Dismiss split because
+            // there's show-when-locked activity showing on top of keyguard. Also make sure the
+            // task contains show-when-locked activity remains on top after split dismissed.
+            final StageTaskListener toTop =
+                    mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
+            exitSplitScreen(toTop);
         }
 
-        if (mainStageVisible) {
+        // When both stage's visibility changed to visible, main stage might receives visibility
+        // changed before side stage if it has higher z-order than side stage. Make sure we only
+        // update main stage's windowing mode with the visibility changed of side stage to prevent
+        // stacking multiple windowing mode transactions which result to flicker issue.
+        if (mainStageVisible && stageListener == mSideStageListener) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             if (sideStageVisible) {
                 // The main stage configuration should to follow split layout when side stage is
                 // visible.
                 mMainStage.updateConfiguration(
                         WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
-            } else {
+            } else if (!mSideStage.mRootTaskInfo.isSleeping) {
                 // We want the main stage configuration to be fullscreen when the side stage isn't
                 // visible.
+                // We should not do it when side stage are not visible due to sleeping display too.
                 mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
             }
             // TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
@@ -487,10 +681,6 @@
             // Make sure the main stage is active.
             mMainStage.activate(getMainStageBounds(), wct);
             mSideStage.setBounds(getSideStageBounds(), wct);
-            // Reorder side stage to the top whenever there's a new child task appeared in side
-            // stage. This is needed to prevent main stage occludes side stage and makes main stage
-            // flipping between fullscreen and multi-window windowing mode.
-            wct.reorder(mSideStage.mRootTaskInfo.token, true);
             mTaskOrganizer.applyTransaction(wct);
         }
     }
@@ -517,32 +707,41 @@
     @Override
     public void onDoubleTappedDivider() {
         setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
-                ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
+                ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
     }
 
     @Override
     public void onBoundsChanging(SplitLayout layout) {
-        final StageTaskListener topLeftStage =
-                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
-        final StageTaskListener bottomRightStage =
-                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-
-        mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
     }
 
     @Override
     public void onBoundsChanged(SplitLayout layout) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        updateWindowBounds(layout, wct);
+        mSyncQueue.queue(wct);
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+    }
+
+    /**
+     * Populates `wct` with operations that match the split windows to the current layout.
+     * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
+     */
+    private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
         layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
-        mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
+    }
+
+    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+        final StageTaskListener topLeftStage =
+                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+        final StageTaskListener bottomRightStage =
+                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+        (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
+                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
     }
 
     @Override
@@ -580,11 +779,18 @@
     public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
         mDisplayAreaInfo = displayAreaInfo;
         if (mSplitLayout != null
-                && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)) {
+                && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
+                && mMainStage.isActive()) {
             onBoundsChanged(mSplitLayout);
         }
     }
 
+    private void onFoldedStateChanged(boolean folded) {
+        if (folded && mMainStage.isActive()) {
+            exitSplitScreen(mMainStage);
+        }
+    }
+
     private Rect getSideStageBounds() {
         return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
                 ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
@@ -672,7 +878,8 @@
     @Override
     public boolean startAnimation(@NonNull IBinder transition,
             @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (transition != mSplitTransitions.mPendingDismiss
                 && transition != mSplitTransitions.mPendingEnter) {
@@ -717,14 +924,14 @@
 
         boolean shouldAnimate = true;
         if (mSplitTransitions.mPendingEnter == transition) {
-            shouldAnimate = startPendingEnterAnimation(transition, info, t);
+            shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
         } else if (mSplitTransitions.mPendingDismiss == transition) {
-            shouldAnimate = startPendingDismissAnimation(transition, info, t);
+            shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
         }
         if (!shouldAnimate) return false;
 
-        mSplitTransitions.playAnimation(transition, info, t, finishCallback,
-                mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+        mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
+                finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
         return true;
     }
 
@@ -754,7 +961,8 @@
 
             // Update local states (before animating).
             setDividerVisibility(true);
-            setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
+            setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
+                    null /* wct */);
             setSplitsVisible(true);
 
             addDividerBarToTransition(info, t, true /* show */);
@@ -860,6 +1068,16 @@
         }
     }
 
+    RemoteAnimationTarget getDividerBarLegacyTarget() {
+        final Rect bounds = mSplitLayout.getDividerBounds();
+        return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
+                mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
+                null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
+                new android.graphics.Point(0, 0) /* position */, bounds, bounds,
+                new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
+                null /* taskInfo */, TYPE_DOCK_DIVIDER);
+    }
+
     @Override
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 0fd8eca62..4f73fee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -71,8 +71,8 @@
     }
 
     private final StageListenerCallbacks mCallbacks;
-    private final SyncTransactionQueue mSyncQueue;
     private final SurfaceSession mSurfaceSession;
+    protected final SyncTransactionQueue mSyncQueue;
 
     protected ActivityManager.RunningTaskInfo mRootTaskInfo;
     protected SurfaceControl mRootLeash;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index c6fb5af..7f42fe9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -16,6 +16,13 @@
 
 package com.android.wm.shell.transition;
 
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
@@ -24,22 +31,34 @@
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.os.IBinder;
+import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.view.Choreographer;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.Transformation;
@@ -61,30 +80,55 @@
 public class DefaultTransitionHandler implements Transitions.TransitionHandler {
     private static final int MAX_ANIMATION_DURATION = 3000;
 
+    /**
+     * Restrict ability of activities overriding transition animation in a way such that
+     * an activity can do it only when the transition happens within a same task.
+     *
+     * @see android.app.Activity#overridePendingTransition(int, int)
+     */
+    private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
+            "persist.wm.disable_custom_task_animation";
+
+    /**
+     * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
+     */
+    static boolean sDisableCustomTaskAnimationProperty =
+            SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+
     private final TransactionPool mTransactionPool;
+    private final Context mContext;
     private final ShellExecutor mMainExecutor;
     private final ShellExecutor mAnimExecutor;
     private final TransitionAnimation mTransitionAnimation;
 
+    private final SurfaceSession mSurfaceSession = new SurfaceSession();
+
     /** Keeps track of the currently-running animations associated with each transition. */
     private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
 
     private final Rect mInsets = new Rect(0, 0, 0, 0);
     private float mTransitionAnimationScaleSetting = 1.0f;
 
+    private final int mCurrentUserId;
+
+    private ScreenRotationAnimation mRotationAnimation;
+
     DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
             @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
         mTransactionPool = transactionPool;
+        mContext = context;
         mMainExecutor = mainExecutor;
         mAnimExecutor = animExecutor;
         mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+        mCurrentUserId = UserHandle.myUserId();
 
         AttributeCache.init(context);
     }
 
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                 "start default transition animation, info = %s", info);
@@ -97,19 +141,38 @@
 
         final Runnable onAnimFinish = () -> {
             if (!animations.isEmpty()) return;
+
+            if (mRotationAnimation != null) {
+                mRotationAnimation.kill();
+                mRotationAnimation = null;
+            }
+
             mAnimations.remove(transition);
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         };
+
+        final int wallpaperTransit = getWallpaperTransitType(info);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
+
+            if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
+                    && (change.getEndRotation() != change.getStartRotation())
+                    && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+                mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+                        mTransactionPool, startTransaction, change, info.getRootLeash());
+                mRotationAnimation.startAnimation(animations, onAnimFinish,
+                        mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+                continue;
+            }
+
             if (change.getMode() == TRANSIT_CHANGE) {
                 // No default animation for this, so just update bounds/position.
-                t.setPosition(change.getLeash(),
+                startTransaction.setPosition(change.getLeash(),
                         change.getEndAbsBounds().left - change.getEndRelOffset().x,
                         change.getEndAbsBounds().top - change.getEndRelOffset().y);
                 if (change.getTaskInfo() != null) {
                     // Skip non-tasks since those usually have null bounds.
-                    t.setWindowCrop(change.getLeash(),
+                    startTransaction.setWindowCrop(change.getLeash(),
                             change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
                 }
             }
@@ -117,12 +180,17 @@
             // Don't animate anything that isn't independent.
             if (!TransitionInfo.isIndependent(change, info)) continue;
 
-            Animation a = loadAnimation(info.getType(), info.getFlags(), change);
+            Animation a = loadAnimation(info, change, wallpaperTransit);
             if (a != null) {
-                startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
+                startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+                        mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+
+                if (info.getAnimationOptions() != null) {
+                    attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+                }
             }
         }
-        t.apply();
+        startTransaction.apply();
         // run finish now in-case there are no animations
         onAnimFinish.run();
         return true;
@@ -141,87 +209,134 @@
     }
 
     @Nullable
-    private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) {
-        // TODO(b/178678389): It should handle more type animation here
+    private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
+            int wallpaperTransit) {
         Animation a = null;
 
-        final boolean isOpening = Transitions.isOpeningType(type);
+        final int type = info.getType();
+        final int flags = info.getFlags();
         final int changeMode = change.getMode();
         final int changeFlags = change.getFlags();
+        final boolean isOpeningType = Transitions.isOpeningType(type);
+        final boolean enter = Transitions.isOpeningType(changeMode);
+        final boolean isTask = change.getTaskInfo() != null;
+        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+        final int overrideType = options != null ? options.getType() : ANIM_NONE;
+        final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
 
-        if (type == TRANSIT_RELAUNCH) {
-            a = mTransitionAnimation.createRelaunchAnimation(
-                    change.getStartAbsBounds(), mInsets, change.getEndAbsBounds());
-        } else if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
+        if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
             a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
                     (changeFlags & FLAG_SHOW_WALLPAPER) != 0);
         } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
             a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
-        } else if (changeMode == TRANSIT_OPEN && isOpening) {
-            if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
-                // This received a transferred starting window, so don't animate
-                return null;
-            }
-
-            if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
-                a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
-            } else if (change.getTaskInfo() != null) {
-                a = mTransitionAnimation.loadDefaultAnimationAttr(
-                        R.styleable.WindowAnimation_taskOpenEnterAnimation);
+        } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
+            if (isOpeningType) {
+                a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
             } else {
-                a = mTransitionAnimation.loadDefaultAnimationRes(
-                        (changeFlags & FLAG_TRANSLUCENT) == 0
-                        ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
-            }
-        } else if (changeMode == TRANSIT_TO_FRONT && isOpening) {
-            if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
-                // This received a transferred starting window, so don't animate
-                return null;
-            }
-
-            if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
-                a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
-            } else {
-                a = mTransitionAnimation.loadDefaultAnimationAttr(
-                        R.styleable.WindowAnimation_taskToFrontEnterAnimation);
-            }
-        } else if (changeMode == TRANSIT_CLOSE && !isOpening) {
-            if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
-                a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
-            } else if (change.getTaskInfo() != null) {
-                a = mTransitionAnimation.loadDefaultAnimationAttr(
-                        R.styleable.WindowAnimation_taskCloseExitAnimation);
-            } else {
-                a = mTransitionAnimation.loadDefaultAnimationRes(
-                        (changeFlags & FLAG_TRANSLUCENT) == 0
-                        ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
-            }
-        } else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
-            if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
-                a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
-            } else {
-                a = mTransitionAnimation.loadDefaultAnimationAttr(
-                        R.styleable.WindowAnimation_taskToBackExitAnimation);
+                a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
             }
         } else if (changeMode == TRANSIT_CHANGE) {
             // In the absence of a specific adapter, we just want to keep everything stationary.
             a = new AlphaAnimation(1.f, 1.f);
             a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+        } else if (type == TRANSIT_RELAUNCH) {
+            a = mTransitionAnimation.createRelaunchAnimation(
+                    change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
+        } else if (overrideType == ANIM_CUSTOM
+                && (canCustomContainer || options.getOverrideTaskTransition())) {
+            a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
+                    ? options.getEnterResId() : options.getExitResId());
+        } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
+            a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
+        } else if (overrideType == ANIM_CLIP_REVEAL) {
+            a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
+                    change.getEndAbsBounds(), change.getEndAbsBounds(),
+                    options.getTransitionBounds());
+        } else if (overrideType == ANIM_SCALE_UP) {
+            a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
+                    change.getEndAbsBounds(), options.getTransitionBounds());
+        } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
+                || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
+            final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
+            a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
+                    change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+                    options.getTransitionBounds());
+        } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
+            // This received a transferred starting window, so don't animate
+            return null;
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
+        } else if (type == TRANSIT_OPEN) {
+            if (isTask) {
+                a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                        ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+                        : R.styleable.WindowAnimation_taskOpenExitAnimation);
+            } else {
+                if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+                    a = mTransitionAnimation.loadDefaultAnimationRes(
+                            R.anim.activity_translucent_open_enter);
+                } else {
+                    a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                            ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+                            : R.styleable.WindowAnimation_activityOpenExitAnimation);
+                }
+            }
+        } else if (type == TRANSIT_TO_FRONT) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+                    : R.styleable.WindowAnimation_taskToFrontExitAnimation);
+        } else if (type == TRANSIT_CLOSE) {
+            if (isTask) {
+                a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                        ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+                        : R.styleable.WindowAnimation_taskCloseExitAnimation);
+            } else {
+                if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+                    a = mTransitionAnimation.loadDefaultAnimationRes(
+                            R.anim.activity_translucent_close_exit);
+                } else {
+                    a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                            ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+                            : R.styleable.WindowAnimation_activityCloseExitAnimation);
+                }
+            }
+        } else if (type == TRANSIT_TO_BACK) {
+            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+                    ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+                    : R.styleable.WindowAnimation_taskToBackExitAnimation);
         }
 
         if (a != null) {
-            Rect start = change.getStartAbsBounds();
-            Rect end = change.getEndAbsBounds();
+            if (!a.isInitialized()) {
+                Rect end = change.getEndAbsBounds();
+                a.initialize(end.width(), end.height(), end.width(), end.height());
+            }
             a.restrictDuration(MAX_ANIMATION_DURATION);
-            a.initialize(end.width(), end.height(), start.width(), start.height());
             a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         }
         return a;
     }
 
-    private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
-            @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
-        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+    static void startSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Animation anim, @NonNull SurfaceControl leash,
+            @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
+            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
+            @Nullable Point position) {
+        final SurfaceControl.Transaction transaction = pool.acquire();
         final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
         final Transformation transformation = new Transformation();
         final float[] matrix = new float[9];
@@ -231,14 +346,16 @@
         va.addUpdateListener(animation -> {
             final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
 
-            applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
+            applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
+                    position);
         });
 
         final Runnable finisher = () -> {
-            applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+            applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
+                    position);
 
-            mTransactionPool.release(transaction);
-            mMainExecutor.execute(() -> {
+            pool.release(transaction);
+            mainExecutor.execute(() -> {
                 animations.remove(va);
                 finishCallback.run();
             });
@@ -255,12 +372,116 @@
             }
         });
         animations.add(va);
-        mAnimExecutor.execute(va::start);
+        animExecutor.execute(va::start);
+    }
+
+    private void attachThumbnail(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, TransitionInfo.Change change,
+            TransitionInfo.AnimationOptions options) {
+        final boolean isTask = change.getTaskInfo() != null;
+        final boolean isOpen = Transitions.isOpeningType(change.getMode());
+        final boolean isClose = Transitions.isClosingType(change.getMode());
+        if (isOpen) {
+            if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
+                attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+            } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
+                attachThumbnailAnimation(animations, finishCallback, change, options);
+            }
+        } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
+            attachThumbnailAnimation(animations, finishCallback, change, options);
+        }
+    }
+
+    private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+        final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
+                ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
+        final Rect bounds = change.getEndAbsBounds();
+        final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
+                thumbnailDrawableRes, bounds);
+        if (thumbnail == null) {
+            return;
+        }
+
+        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+        final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+                change.getLeash(), thumbnail, transaction);
+        final Animation a =
+                mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
+        if (a == null) {
+            return;
+        }
+
+        final Runnable finisher = () -> {
+            wt.destroy(transaction);
+            mTransactionPool.release(transaction);
+
+            finishCallback.run();
+        };
+        a.restrictDuration(MAX_ANIMATION_DURATION);
+        a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+                mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+    }
+
+    private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, TransitionInfo.Change change,
+            TransitionInfo.AnimationOptions options) {
+        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+        final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+                change.getLeash(), options.getThumbnail(), transaction);
+        final Rect bounds = change.getEndAbsBounds();
+        final int orientation = mContext.getResources().getConfiguration().orientation;
+        final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds,
+                mInsets, options.getThumbnail(), orientation, null /* startRect */,
+                options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP);
+
+        final Runnable finisher = () -> {
+            wt.destroy(transaction);
+            mTransactionPool.release(transaction);
+
+            finishCallback.run();
+        };
+        a.restrictDuration(MAX_ANIMATION_DURATION);
+        a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+                mMainExecutor, mAnimExecutor, null /* position */);
+    }
+
+    private static int getWallpaperTransitType(TransitionInfo info) {
+        boolean hasOpenWallpaper = false;
+        boolean hasCloseWallpaper = false;
+
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
+                if (Transitions.isOpeningType(change.getMode())) {
+                    hasOpenWallpaper = true;
+                } else if (Transitions.isClosingType(change.getMode())) {
+                    hasCloseWallpaper = true;
+                }
+            }
+        }
+
+        if (hasOpenWallpaper && hasCloseWallpaper) {
+            return Transitions.isOpeningType(info.getType())
+                    ? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
+        } else if (hasOpenWallpaper) {
+            return WALLPAPER_TRANSITION_OPEN;
+        } else if (hasCloseWallpaper) {
+            return WALLPAPER_TRANSITION_CLOSE;
+        } else {
+            return WALLPAPER_TRANSITION_NONE;
+        }
     }
 
     private static void applyTransformation(long time, SurfaceControl.Transaction t,
-            SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+            SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
+            Point position) {
         anim.getTransformation(time, transformation);
+        if (position != null) {
+            transformation.getMatrix().postTranslate(position.x, position.y);
+        }
         t.setMatrix(leash, transformation.getMatrix(), matrix);
         t.setAlpha(leash, transformation.getAlpha());
         t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
new file mode 100644
index 0000000..61e11e8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.transition;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IWindowContainerTransactionCallback;
+
+/**
+ * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
+ * synctransaction tools.
+ */
+public class LegacyTransitions {
+
+    /**
+     * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
+     * into one callback.
+     */
+    public interface ILegacyTransition {
+        /**
+         * Called when both the associated sync transaction finishes and the remote animation is
+         * ready.
+         */
+        void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
+    }
+
+    /**
+     * Makes sure that a remote animation and corresponding sync callback are called together
+     * such that the sync callback is called first. This assumes that both the callback receiver
+     * and the remoteanimation are in the same process so that order is preserved on both ends.
+     */
+    public static class LegacyTransition {
+        private final ILegacyTransition mLegacyTransition;
+        private int mSyncId = -1;
+        private SurfaceControl.Transaction mTransaction;
+        private int mTransit;
+        private RemoteAnimationTarget[] mApps;
+        private RemoteAnimationTarget[] mWallpapers;
+        private RemoteAnimationTarget[] mNonApps;
+        private IRemoteAnimationFinishedCallback mFinishCallback = null;
+        private boolean mCancelled = false;
+        private final SyncCallback mSyncCallback = new SyncCallback();
+        private final RemoteAnimationAdapter mAdapter =
+                new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
+
+        public LegacyTransition(@WindowManager.TransitionType int type,
+                @NonNull ILegacyTransition legacyTransition) {
+            mLegacyTransition = legacyTransition;
+            mTransit = type;
+        }
+
+        public @WindowManager.TransitionType int getType() {
+            return mTransit;
+        }
+
+        public IWindowContainerTransactionCallback getSyncCallback() {
+            return mSyncCallback;
+        }
+
+        public RemoteAnimationAdapter getAdapter() {
+            return mAdapter;
+        }
+
+        private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
+            @Override
+            public void onTransactionReady(int id, SurfaceControl.Transaction t)
+                    throws RemoteException {
+                mSyncId = id;
+                mTransaction = t;
+                checkApply();
+            }
+        }
+
+        private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
+            @Override
+            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+                    IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+                mTransit = transit;
+                mApps = apps;
+                mWallpapers = wallpapers;
+                mNonApps = nonApps;
+                mFinishCallback = finishedCallback;
+                checkApply();
+            }
+
+            @Override
+            public void onAnimationCancelled() throws RemoteException {
+                mCancelled = true;
+                mApps = mWallpapers = mNonApps = null;
+                checkApply();
+            }
+        }
+
+
+        private void checkApply() throws RemoteException {
+            if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) return;
+            mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
+                    mNonApps, mFinishCallback, mTransaction);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4da6664..6bd8053 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -57,7 +57,8 @@
 
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (mTransition != transition) return false;
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
@@ -70,19 +71,24 @@
         };
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
-            public void onTransitionFinished(WindowContainerTransaction wct) {
+            public void onTransitionFinished(WindowContainerTransaction wct,
+                    SurfaceControl.Transaction sct) {
                 if (mRemote.asBinder() != null) {
                     mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                 }
-                mMainExecutor.execute(
-                        () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+                mMainExecutor.execute(() -> {
+                    if (sct != null) {
+                        finishTransaction.merge(sct);
+                    }
+                    finishCallback.onTransitionFinished(wct, null /* wctCB */);
+                });
             }
         };
         try {
             if (mRemote.asBinder() != null) {
                 mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
             }
-            mRemote.startAnimation(transition, info, t, cb);
+            mRemote.startAnimation(transition, info, startTransaction, cb);
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error running remote transition.", e);
             if (mRemote.asBinder() != null) {
@@ -102,7 +108,8 @@
 
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
-            public void onTransitionFinished(WindowContainerTransaction wct) {
+            public void onTransitionFinished(WindowContainerTransaction wct,
+                    SurfaceControl.Transaction sct) {
                 mMainExecutor.execute(
                         () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9bfb261..f432049 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -56,14 +56,7 @@
     private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
             new ArrayList<>();
 
-    private final IBinder.DeathRecipient mTransitionDeathRecipient =
-            new IBinder.DeathRecipient() {
-                @Override
-                @BinderThread
-                public void binderDied() {
-                    mMainExecutor.execute(() -> mFilters.clear());
-                }
-            };
+    private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>();
 
     RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) {
         mMainExecutor = mainExecutor;
@@ -71,7 +64,9 @@
 
     void addFiltered(TransitionFilter filter, IRemoteTransition remote) {
         try {
-            remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+            RemoteDeathHandler handler = new RemoteDeathHandler(remote.asBinder());
+            remote.asBinder().linkToDeath(handler, 0 /* flags */);
+            mDeathHandlers.put(remote.asBinder(), handler);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to link to death");
             return;
@@ -88,7 +83,8 @@
             }
         }
         if (removed) {
-            remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+            RemoteDeathHandler handler = mDeathHandlers.remove(remote.asBinder());
+            remote.asBinder().unlinkToDeath(handler, 0 /* flags */);
         }
     }
 
@@ -99,7 +95,8 @@
 
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
         if (pendingRemote == null) {
@@ -132,11 +129,15 @@
         };
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
-            public void onTransitionFinished(WindowContainerTransaction wct) {
+            public void onTransitionFinished(WindowContainerTransaction wct,
+                    SurfaceControl.Transaction sct) {
                 if (remote.asBinder() != null) {
                     remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                 }
                 mMainExecutor.execute(() -> {
+                    if (sct != null) {
+                        finishTransaction.merge(sct);
+                    }
                     mRequestedRemotes.remove(transition);
                     finishCallback.onTransitionFinished(wct, null /* wctCB */);
                 });
@@ -146,7 +147,7 @@
             if (remote.asBinder() != null) {
                 remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
             }
-            remote.startAnimation(transition, info, t, cb);
+            remote.startAnimation(transition, info, startTransaction, cb);
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error running remote transition.", e);
             if (remote.asBinder() != null) {
@@ -170,7 +171,8 @@
 
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
-            public void onTransitionFinished(WindowContainerTransaction wct) {
+            public void onTransitionFinished(WindowContainerTransaction wct,
+                    SurfaceControl.Transaction sct) {
                 mMainExecutor.execute(() -> {
                     if (!mRequestedRemotes.containsKey(mergeTarget)) {
                         Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
@@ -200,4 +202,25 @@
                 + " for %s: %s", transition, remote);
         return new WindowContainerTransaction();
     }
+
+    /** NOTE: binder deaths can alter the filter order */
+    private class RemoteDeathHandler implements IBinder.DeathRecipient {
+        private final IBinder mRemote;
+
+        RemoteDeathHandler(IBinder remote) {
+            mRemote = remote;
+        }
+
+        @Override
+        @BinderThread
+        public void binderDied() {
+            mMainExecutor.execute(() -> {
+                for (int i = mFilters.size() - 1; i >= 0; --i) {
+                    if (mRemote.equals(mFilters.get(i).second.asBinder())) {
+                        mFilters.remove(i);
+                    }
+                }
+            });
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
new file mode 100644
index 0000000..a13b03d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.transition;
+
+import static android.hardware.HardwareBuffer.RGBA_8888;
+import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
+import static android.util.RotationUtils.deltaRotation;
+
+import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
+import static com.android.wm.shell.transition.Transitions.TAG;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceSession;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.window.TransitionInfo;
+
+import com.android.internal.R;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class handles the rotation animation when the device is rotated.
+ *
+ * <p>
+ * The screen rotation animation is composed of 4 different part:
+ * <ul>
+ * <li> The screenshot: <p>
+ *     A screenshot of the whole screen prior the change of orientation is taken to hide the
+ *     element resizing below. The screenshot is then animated to rotate and cross-fade to
+ *     the new orientation with the content in the new orientation.
+ *
+ * <li> The windows on the display: <p>y
+ *      Once the device is rotated, the screen and its content are in the new orientation. The
+ *      animation first rotate the new content into the old orientation to then be able to
+ *      animate to the new orientation
+ *
+ * <li> The Background color frame: <p>
+ *      To have the animation seem more seamless, we add a color transitioning background behind the
+ *      exiting and entering layouts. We compute the brightness of the start and end
+ *      layouts and transition from the two brightness values as grayscale underneath the animation
+ *
+ * <li> The entering Blackframe: <p>
+ *     The enter Blackframe is similar to the exit Blackframe but is only used when a custom
+ *     rotation animation is used and matches the new content size instead of the screenshot.
+ * </ul>
+ */
+class ScreenRotationAnimation {
+
+    /** How much to multiply the policy's type layer, to reserve room
+     * for multiple windows of the same type and Z-ordering adjustment
+     * with TYPE_LAYER_OFFSET. */
+    static final int TYPE_LAYER_MULTIPLIER = 10000;
+    static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+
+    /*
+     * Layers for screen rotation animation. We put these layers above
+     * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+     */
+    private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
+
+    static final int MAX_ANIMATION_DURATION = 10 * 1000;
+
+    private final Context mContext;
+    private final TransactionPool mTransactionPool;
+    private final float[] mTmpFloats = new float[9];
+    // Complete transformations being applied.
+    private final Matrix mSnapshotInitialMatrix = new Matrix();
+    /** The leash of display. */
+    private final SurfaceControl mSurfaceControl;
+    private final Rect mStartBounds = new Rect();
+    private final Rect mEndBounds = new Rect();
+
+    private final int mStartWidth;
+    private final int mStartHeight;
+    private final int mEndWidth;
+    private final int mEndHeight;
+    private final int mStartRotation;
+    private final int mEndRotation;
+
+    /** This layer contains the actual screenshot that is to be faded out. */
+    private SurfaceControl mScreenshotLayer;
+    /**
+     * Only used for screen rotation and not custom animations. Layered behind all other layers
+     * to avoid showing any "empty" spots
+     */
+    private SurfaceControl mBackColorSurface;
+    /** The leash using to animate screenshot layer. */
+    private SurfaceControl mAnimLeash;
+    private Transaction mTransaction;
+
+    // The current active animation to move from the old to the new rotated
+    // state.  Which animation is run here will depend on the old and new
+    // rotations.
+    private Animation mRotateExitAnimation;
+    private Animation mRotateEnterAnimation;
+
+    /** Intensity of light/whiteness of the layout before rotation occurs. */
+    private float mStartLuma;
+    /** Intensity of light/whiteness of the layout after rotation occurs. */
+    private float mEndLuma;
+
+    ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
+            Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash) {
+        mContext = context;
+        mTransactionPool = pool;
+
+        mSurfaceControl = change.getLeash();
+        mStartWidth = change.getStartAbsBounds().width();
+        mStartHeight = change.getStartAbsBounds().height();
+        mEndWidth = change.getEndAbsBounds().width();
+        mEndHeight = change.getEndAbsBounds().height();
+        mStartRotation = change.getStartRotation();
+        mEndRotation = change.getEndRotation();
+
+        mStartBounds.set(change.getStartAbsBounds());
+        mEndBounds.set(change.getEndAbsBounds());
+
+        mAnimLeash = new SurfaceControl.Builder(session)
+                .setParent(rootLeash)
+                .setEffectLayer()
+                .setCallsite("ShellRotationAnimation")
+                .setName("Animation leash of screenshot rotation")
+                .build();
+
+        try {
+            SurfaceControl.LayerCaptureArgs args =
+                    new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+                            .setCaptureSecureLayers(true)
+                            .setAllowProtected(true)
+                            .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
+                            .build();
+            SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                    SurfaceControl.captureLayers(args);
+            if (screenshotBuffer == null) {
+                Slog.w(TAG, "Unable to take screenshot of display");
+                return;
+            }
+
+            mBackColorSurface = new SurfaceControl.Builder(session)
+                    .setParent(rootLeash)
+                    .setColorLayer()
+                    .setCallsite("ShellRotationAnimation")
+                    .setName("BackColorSurface")
+                    .build();
+
+            mScreenshotLayer = new SurfaceControl.Builder(session)
+                    .setParent(mAnimLeash)
+                    .setBLASTLayer()
+                    .setSecure(screenshotBuffer.containsSecureLayers())
+                    .setCallsite("ShellRotationAnimation")
+                    .setName("RotationLayer")
+                    .build();
+
+            HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+            mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
+
+            GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
+                    screenshotBuffer.getHardwareBuffer());
+
+            t.setLayer(mBackColorSurface, -1);
+            t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+            t.setAlpha(mBackColorSurface, 1);
+            t.show(mBackColorSurface);
+
+            t.setPosition(mAnimLeash, 0, 0);
+            t.setAlpha(mAnimLeash, 1);
+            t.show(mAnimLeash);
+
+            t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+            t.setBuffer(mScreenshotLayer, buffer);
+            t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
+            t.show(mScreenshotLayer);
+
+        } catch (Surface.OutOfResourcesException e) {
+            Slog.w(TAG, "Unable to allocate freeze surface", e);
+        }
+
+        setRotation(t);
+        t.apply();
+    }
+
+    private void setRotation(SurfaceControl.Transaction t) {
+        // Compute the transformation matrix that must be applied
+        // to the snapshot to make it stay in the same original position
+        // with the current screen rotation.
+        int delta = deltaRotation(mEndRotation, mStartRotation);
+        createRotationMatrix(delta, mStartWidth, mStartHeight, mSnapshotInitialMatrix);
+        setRotationTransform(t, mSnapshotInitialMatrix);
+    }
+
+    private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
+        if (mScreenshotLayer == null) {
+            return;
+        }
+        matrix.getValues(mTmpFloats);
+        float x = mTmpFloats[Matrix.MTRANS_X];
+        float y = mTmpFloats[Matrix.MTRANS_Y];
+        t.setPosition(mScreenshotLayer, x, y);
+        t.setMatrix(mScreenshotLayer,
+                mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+                mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+
+        t.setAlpha(mScreenshotLayer, (float) 1.0);
+        t.show(mScreenshotLayer);
+    }
+
+    /**
+     * Returns true if animating.
+     */
+    public boolean startAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, float animationScale,
+            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+        if (mScreenshotLayer == null) {
+            // Can't do animation.
+            return false;
+        }
+
+        // TODO : Found a way to get right end luma and re-enable color frame animation.
+        // End luma value is very not stable so it will cause more flicker is we run background
+        // color frame animation.
+        //mEndLuma = getLumaOfSurfaceControl(mEndBounds, mSurfaceControl);
+
+        // Figure out how the screen has moved from the original rotation.
+        int delta = deltaRotation(mEndRotation, mStartRotation);
+        switch (delta) { /* Counter-Clockwise Rotations */
+            case Surface.ROTATION_0:
+                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_0_exit);
+                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.rotation_animation_enter);
+                break;
+            case Surface.ROTATION_90:
+                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_plus_90_exit);
+                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_plus_90_enter);
+                break;
+            case Surface.ROTATION_180:
+                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_180_exit);
+                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_180_enter);
+                break;
+            case Surface.ROTATION_270:
+                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_minus_90_exit);
+                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+                        R.anim.screen_rotate_minus_90_enter);
+                break;
+        }
+
+        mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+        mRotateExitAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+        mRotateExitAnimation.scaleCurrentDuration(animationScale);
+        mRotateEnterAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+        mRotateEnterAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+        mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
+        mTransaction = mTransactionPool.acquire();
+        startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+        startScreenshotRotationAnimation(animations, finishCallback, mainExecutor, animExecutor);
+        //startColorAnimation(mTransaction, animationScale);
+
+        return true;
+    }
+
+    private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+            @NonNull ShellExecutor animExecutor) {
+        startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
+                mTransactionPool, mainExecutor, animExecutor, null /* position */);
+    }
+
+    private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+            @NonNull ShellExecutor animExecutor) {
+        startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
+                mTransactionPool, mainExecutor, animExecutor, null /* position */);
+    }
+
+    private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
+        int colorTransitionMs = mContext.getResources().getInteger(
+                R.integer.config_screen_rotation_color_transition);
+        final float[] rgbTmpFloat = new float[3];
+        final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+        final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+        final long duration = colorTransitionMs * (long) animationScale;
+        final Transaction t = mTransactionPool.acquire();
+
+        final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+        // Animation length is already expected to be scaled.
+        va.overrideDurationScale(1.0f);
+        va.setDuration(duration);
+        va.addUpdateListener(animation -> {
+            final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+            final float fraction = currentPlayTime / va.getDuration();
+            applyColor(startColor, endColor, rgbTmpFloat, fraction, mBackColorSurface, t);
+        });
+        va.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+                        t);
+                mTransactionPool.release(t);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+                        t);
+                mTransactionPool.release(t);
+            }
+        });
+        animExecutor.execute(va::start);
+    }
+
+    public void kill() {
+        Transaction t = mTransaction != null ? mTransaction : mTransactionPool.acquire();
+        if (mAnimLeash.isValid()) {
+            t.remove(mAnimLeash);
+        }
+
+        if (mScreenshotLayer != null) {
+            if (mScreenshotLayer.isValid()) {
+                t.remove(mScreenshotLayer);
+            }
+            mScreenshotLayer = null;
+
+            if (mBackColorSurface != null) {
+                if (mBackColorSurface.isValid()) {
+                    t.remove(mBackColorSurface);
+                }
+                mBackColorSurface = null;
+            }
+        }
+        t.apply();
+        mTransactionPool.release(t);
+    }
+
+    /**
+     * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the
+     * luminance at the borders of the bitmap
+     * @return the average luminance of all the pixels at the borders of the bitmap
+     */
+    private static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) {
+        // Cannot read content from buffer with protected usage.
+        if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888
+                || hasProtectedContent(hardwareBuffer)) {
+            return 0;
+        }
+
+        ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(),
+                hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1);
+        ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace);
+        Image image = ir.acquireLatestImage();
+        if (image == null || image.getPlanes().length == 0) {
+            return 0;
+        }
+
+        Image.Plane plane = image.getPlanes()[0];
+        ByteBuffer buffer = plane.getBuffer();
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int pixelStride = plane.getPixelStride();
+        int rowStride = plane.getRowStride();
+        float[] borderLumas = new float[2 * width + 2 * height];
+
+        // Grab the top and bottom borders
+        int l = 0;
+        for (int x = 0; x < width; x++) {
+            borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride);
+            borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride);
+        }
+
+        // Grab the left and right borders
+        for (int y = 0; y < height; y++) {
+            borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride);
+            borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride);
+        }
+
+        // Cleanup
+        ir.close();
+
+        // Oh, is this too simple and inefficient for you?
+        // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians
+        Arrays.sort(borderLumas);
+        return borderLumas[borderLumas.length / 2];
+    }
+
+    /**
+     * @return whether the hardwareBuffer passed in is marked as protected.
+     */
+    private static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) {
+        return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT;
+    }
+
+    private static float getPixelLuminance(ByteBuffer buffer, int x, int y,
+            int pixelStride, int rowStride) {
+        int offset = y * rowStride + x * pixelStride;
+        int pixel = 0;
+        pixel |= (buffer.get(offset) & 0xff) << 16;     // R
+        pixel |= (buffer.get(offset + 1) & 0xff) << 8;  // G
+        pixel |= (buffer.get(offset + 2) & 0xff);       // B
+        pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
+        return Color.valueOf(pixel).luminance();
+    }
+
+    /**
+     * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
+     * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace)
+     */
+    private static float getLumaOfSurfaceControl(Rect bounds, SurfaceControl surfaceControl) {
+        if (surfaceControl ==  null) {
+            return 0;
+        }
+
+        Rect crop = new Rect(0, 0, bounds.width(), bounds.height());
+        SurfaceControl.ScreenshotHardwareBuffer buffer =
+                SurfaceControl.captureLayers(surfaceControl, crop, 1);
+        if (buffer == null) {
+            return 0;
+        }
+
+        return getMedianBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace());
+    }
+
+    private static void createRotationMatrix(int rotation, int width, int height,
+            Matrix outMatrix) {
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                outMatrix.reset();
+                break;
+            case Surface.ROTATION_90:
+                outMatrix.setRotate(90, 0, 0);
+                outMatrix.postTranslate(height, 0);
+                break;
+            case Surface.ROTATION_180:
+                outMatrix.setRotate(180, 0, 0);
+                outMatrix.postTranslate(width, height);
+                break;
+            case Surface.ROTATION_270:
+                outMatrix.setRotate(270, 0, 0);
+                outMatrix.postTranslate(0, width);
+                break;
+        }
+    }
+
+    private static void applyColor(int startColor, int endColor, float[] rgbFloat,
+            float fraction, SurfaceControl surface, SurfaceControl.Transaction t) {
+        final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor,
+                endColor);
+        Color middleColor = Color.valueOf(color);
+        rgbFloat[0] = middleColor.red();
+        rgbFloat[1] = middleColor.green();
+        rgbFloat[2] = middleColor.blue();
+        if (surface.isValid()) {
+            t.setColor(surface, rgbFloat);
+        }
+        t.apply();
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 60707cc..01ef2a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -77,6 +77,15 @@
     /** Transition type for launching 2 tasks simultaneously. */
     public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
 
+    /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
+    public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+    /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
+    public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+
+    /** Transition type for entering split by opening an app into side-stage. */
+    public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+
     private final WindowOrganizer mOrganizer;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
@@ -91,12 +100,13 @@
     private float mTransitionAnimationScaleSetting = 1.0f;
 
     private static final class ActiveTransition {
-        IBinder mToken = null;
-        TransitionHandler mHandler = null;
-        boolean mMerged = false;
-        TransitionInfo mInfo = null;
-        SurfaceControl.Transaction mStartT = null;
-        SurfaceControl.Transaction mFinishT = null;
+        IBinder mToken;
+        TransitionHandler mHandler;
+        boolean mMerged;
+        boolean mAborted;
+        TransitionInfo mInfo;
+        SurfaceControl.Transaction mStartT;
+        SurfaceControl.Transaction mFinishT;
     }
 
     /** Keeps track of currently playing transitions in the order of receipt. */
@@ -382,7 +392,7 @@
     }
 
     boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
-        return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+        return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
                 (wct, cb) -> onFinish(active.mToken, wct, cb));
     }
 
@@ -416,17 +426,19 @@
 
     /** Special version of finish just for dealing with no-op/invalid transitions. */
     private void onAbort(IBinder transition) {
-        final int activeIdx = findActiveTransition(transition);
-        if (activeIdx < 0) return;
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
-                "Transition animation aborted due to no-op, notifying core %s", transition);
-        mActiveTransitions.remove(activeIdx);
-        mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+        onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
     }
 
     private void onFinish(IBinder transition,
             @Nullable WindowContainerTransaction wct,
             @Nullable WindowContainerTransactionCallback wctCB) {
+        onFinish(transition, wct, wctCB, false /* abort */);
+    }
+
+    private void onFinish(IBinder transition,
+            @Nullable WindowContainerTransaction wct,
+            @Nullable WindowContainerTransactionCallback wctCB,
+            boolean abort) {
         int activeIdx = findActiveTransition(transition);
         if (activeIdx < 0) {
             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
@@ -434,28 +446,37 @@
             return;
         } else if (activeIdx > 0) {
             // This transition was merged.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
-                    transition);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
+                    + " %s", abort, transition);
             final ActiveTransition active = mActiveTransitions.get(activeIdx);
             active.mMerged = true;
+            active.mAborted = abort;
             if (active.mHandler != null) {
                 active.mHandler.onTransitionMerged(active.mToken);
             }
             return;
         }
+        mActiveTransitions.get(activeIdx).mAborted = abort;
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
-                "Transition animation finished, notifying core %s", transition);
+                "Transition animation finished (abort=%b), notifying core %s", abort, transition);
         // Merge all relevant transactions together
         SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
         for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
             final ActiveTransition toMerge = mActiveTransitions.get(iA);
             if (!toMerge.mMerged) break;
+            // aborted transitions have no start/finish transactions
+            if (mActiveTransitions.get(iA).mStartT == null) break;
+            if (fullFinish == null) {
+                fullFinish = new SurfaceControl.Transaction();
+            }
             // Include start. It will be a no-op if it was already applied. Otherwise, we need it
             // to maintain consistent state.
             fullFinish.merge(mActiveTransitions.get(iA).mStartT);
             fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
         }
-        fullFinish.apply();
+        if (fullFinish != null) {
+            fullFinish.apply();
+        }
         // Now perform all the finishes.
         mActiveTransitions.remove(activeIdx);
         mOrganizer.finishTransition(transition, wct, wctCB);
@@ -464,6 +485,12 @@
             ActiveTransition merged = mActiveTransitions.remove(activeIdx);
             mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
         }
+        // sift through aborted transitions
+        while (mActiveTransitions.size() > activeIdx
+                && mActiveTransitions.get(activeIdx).mAborted) {
+            ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
+            mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
+        }
         if (mActiveTransitions.size() <= activeIdx) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
                     + "finished");
@@ -494,6 +521,12 @@
         int mergeIdx = activeIdx + 1;
         while (mergeIdx < mActiveTransitions.size()) {
             ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+            if (mergeCandidate.mAborted) {
+                // transition was aborted, so we can skip for now (still leave it in the list
+                // so that it gets cleaned-up in the right order).
+                ++mergeIdx;
+                continue;
+            }
             if (mergeCandidate.mMerged) {
                 throw new IllegalStateException("Can't merge a transition after not-merging"
                         + " a preceding one.");
@@ -566,12 +599,19 @@
          * Starts a transition animation. This is always called if handleRequest returned non-null
          * for a particular transition. Otherwise, it is only called if no other handler before
          * it handled the transition.
-         *
+         * @param startTransaction the transaction given to the handler to be applied before the
+         *                         transition animation. Note the handler is expected to call on
+         *                         {@link SurfaceControl.Transaction#apply()} for startTransaction.
+         * @param finishTransaction the transaction given to the handler to be applied after the
+         *                       transition animation. Unlike startTransaction, the handler is NOT
+         *                       expected to apply this transaction. The Transition system will
+         *                       apply it when finishCallback is called.
          * @param finishCallback Call this when finished. This MUST be called on main thread.
          * @return true if transition was handled, false if not (falls-back to default).
          */
         boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
                 @NonNull TransitionFinishCallback finishCallback);
 
         /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
new file mode 100644
index 0000000..2c668ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.transition;
+
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * Represents a surface that is displayed over a transition surface.
+ */
+class WindowThumbnail {
+
+    private SurfaceControl mSurfaceControl;
+
+    private WindowThumbnail() {}
+
+    /** Create a thumbnail surface and attach it over a parent surface. */
+    static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
+            HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
+        WindowThumbnail windowThumbnail = new WindowThumbnail();
+        windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+                .setParent(parent)
+                .setName("WindowThumanil : " + parent.toString())
+                .setCallsite("WindowThumanil")
+                .setFormat(PixelFormat.TRANSLUCENT)
+                .build();
+
+        GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
+        t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer);
+        t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
+        t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE);
+        t.show(windowThumbnail.mSurfaceControl);
+        t.apply();
+
+        return windowThumbnail;
+    }
+
+    SurfaceControl getSurface() {
+        return mSurfaceControl;
+    }
+
+    /** Remove the thumbnail surface and release the surface. */
+    void destroy(SurfaceControl.Transaction t) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+
+        t.remove(mSurfaceControl);
+        t.apply();
+        mSurfaceControl.release();
+        mSurfaceControl = null;
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 9dd25fe..3ca5b9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -25,11 +25,17 @@
 
 android_test {
     name: "WMShellFlickerTests",
-    srcs: ["src/**/*.java", "src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     manifest: "AndroidManifest.xml",
     test_config: "AndroidTest.xml",
     platform_apis: true,
     certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
     test_suites: ["device-tests"],
     libs: ["android.test.runner"],
     static_libs: [
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index c5b5b91..b36468b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -16,97 +16,100 @@
 
 package com.android.wm.shell.flicker
 
+import android.content.ComponentName
 import android.graphics.Region
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
 
-fun FlickerTestParameter.appPairsDividerIsVisible() {
+fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
     assertLayersEnd {
-        this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+        this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.appPairsDividerIsInvisible() {
+fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() {
     assertLayersEnd {
-        this.notContains(APP_PAIR_SPLIT_DIVIDER)
+        this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.appPairsDividerBecomesVisible() {
     assertLayers {
-        this.isInvisible(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isVisible(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.dockedStackDividerIsVisible() {
+fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
     assertLayersEnd {
-        this.isVisible(DOCKED_STACK_DIVIDER)
+        this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
     assertLayers {
-        this.isInvisible(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isVisible(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
     assertLayers {
-        this.isVisible(DOCKED_STACK_DIVIDER)
+        this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isInvisible(DOCKED_STACK_DIVIDER)
+            .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.dockedStackDividerIsInvisible() {
+fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() {
     assertLayersEnd {
-        this.notContains(DOCKED_STACK_DIVIDER)
+        this.notContains(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
+fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
+    rotation: Int,
+    primaryComponent: ComponentName
+) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        visibleRegion(primaryLayerName)
+        val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(primaryComponent)
             .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    primaryLayerName: String
+    primaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        visibleRegion(primaryLayerName)
+        val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(primaryComponent)
             .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible(
+fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    secondaryLayerName: String
+    secondaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        visibleRegion(secondaryLayerName)
+        val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(secondaryComponent)
             .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    secondaryLayerName: String
+    secondaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        visibleRegion(secondaryLayerName)
+        val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(secondaryComponent)
             .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 03b93c7..ff1a6e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonConstants")
 package com.android.wm.shell.flicker
 
-const val IME_WINDOW_NAME = "InputMethod"
+import android.content.ComponentName
+
 const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
-const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
\ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#")
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index ef9f742..19374ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
             }
         }
 
@@ -85,15 +84,13 @@
     @Test
     override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+    fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
 
     @Presubmit
     @Test
@@ -103,8 +100,8 @@
             "Non resizeable app not initialized"
         }
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
-            isInvisible(primaryApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
+            isInvisible(primaryApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index db63c4c..46ee892 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,10 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,10 +53,14 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
             }
         }
 
+    @Presubmit
+    @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -68,14 +71,14 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-            isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+            isVisible(secondaryApp.component)
         }
     }
 
@@ -83,10 +86,10 @@
     @Test
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
-            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            visibleRegion(primaryApp.defaultWindowName)
+            val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+            visibleRegion(primaryApp.component)
                 .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
-            visibleRegion(secondaryApp.defaultWindowName)
+            visibleRegion(secondaryApp.component)
                 .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index c8d3423..f7ced71 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                         composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
             }
         }
 
@@ -77,6 +76,10 @@
         resetMultiWindowConfig(instrumentation)
     }
 
+    @Presubmit
+    @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -87,7 +90,7 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
@@ -97,8 +100,8 @@
             "Non resizeable app not initialized"
         }
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
-            isVisible(primaryApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
+            isVisible(primaryApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 83df836..3debdd3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -25,10 +25,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,9 +51,11 @@
         get() = {
             super.transition(this, it)
             setup {
-                executeShellCommand(
-                    composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                eachRun {
+                    executeShellCommand(
+                            composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+                    waitAppsShown(primaryApp, secondaryApp)
+                }
             }
             transitions {
                 // TODO pair apps through normal UX flow
@@ -73,14 +75,14 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+    fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppWindowsInvisible() {
         testSpec.assertWmEnd {
-            isInvisible(primaryApp.defaultWindowName)
-            isInvisible(secondaryApp.defaultWindowName)
+            isInvisible(primaryApp.component)
+            isInvisible(secondaryApp.component)
         }
     }
 
@@ -88,10 +90,10 @@
     @Test
     fun appsStartingBounds() {
         testSpec.assertLayersStart {
-            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            visibleRegion(primaryApp.defaultWindowName)
+            val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+            visibleRegion(primaryApp.component)
                 .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
-            visibleRegion(secondaryApp.defaultWindowName)
+            visibleRegion(secondaryApp.component)
                 .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
@@ -100,16 +102,14 @@
     @Test
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
-            notContains(primaryApp.defaultWindowName)
-            notContains(secondaryApp.defaultWindowName)
+            notContains(primaryApp.component)
+            notContains(secondaryApp.component)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 1935bb9..cdf89a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -30,14 +30,14 @@
 import com.android.server.wm.flicker.helpers.isRotated
 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.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 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.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.BaseAppHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -154,26 +154,26 @@
 
     @FlakyTest(bugId = 186510496)
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index c875c00..3e782e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -28,10 +27,10 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -57,41 +56,43 @@
             transitions {
                 executeShellCommand(composePairsCommand(
                     primaryTaskId, secondaryTaskId, true /* pair */))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
                 setRotation(testSpec.config.endRotation)
             }
         }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+    @Presubmit
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-                .isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+                .isVisible(secondaryApp.component)
         }
     }
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
-    @FlakyTest(bugId = 172776659)
+    @Presubmit
     @Test
-    fun appPairsPrimaryBoundsIsVisible() =
-        testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
-            primaryApp.defaultWindowName)
+    fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            primaryApp.component)
 
-    @FlakyTest(bugId = 172776659)
+    @FlakyTest
     @Test
-    fun appPairsSecondaryBoundsIsVisible() =
-        testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
-            secondaryApp.defaultWindowName)
+    fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            secondaryApp.component)
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index c3360ca..ee28c7a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -28,12 +27,10 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -60,48 +57,50 @@
                 this.setRotation(testSpec.config.endRotation)
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
             }
         }
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-            isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+            isVisible(secondaryApp.component)
         }
     }
 
     @FlakyTest(bugId = 172776659)
     @Test
-    fun appPairsPrimaryBoundsIsVisible() =
-        testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
-            primaryApp.defaultWindowName)
+    fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            primaryApp.component)
 
     @FlakyTest(bugId = 172776659)
     @Test
-    fun appPairsSecondaryBoundsIsVisible() =
-        testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
-            secondaryApp.defaultWindowName)
+    fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            secondaryApp.component)
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 512fd9a..b95193a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -22,7 +22,10 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 import org.junit.Test
 
 abstract class RotateTwoLaunchedAppsTransition(
@@ -37,8 +40,8 @@
                 test {
                     device.wakeUpAndGoToHomeScreen()
                     this.setRotation(Surface.ROTATION_0)
-                    primaryApp.launchViaIntent()
-                    secondaryApp.launchViaIntent()
+                    primaryApp.launchViaIntent(wmHelper)
+                    secondaryApp.launchViaIntent(wmHelper)
                     updateTasksId()
                 }
             }
@@ -52,10 +55,17 @@
             }
         }
 
+    @Before
+    override fun setup() {
+        // AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation.
+        assumeFalse(isShellTransitionsEnabled())
+        super.setup()
+    }
+
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
     }
 
     @FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5b8cfb8..5a438af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.content.ComponentName
 import android.graphics.Region
+import com.android.server.wm.flicker.Flicker
 import com.android.server.wm.flicker.helpers.WindowUtils
 
 class AppPairsHelper(
@@ -43,5 +44,17 @@
     companion object {
         const val TEST_REPETITIONS = 1
         const val TIMEOUT_MS = 3_000L
+
+        fun Flicker.waitAppsShown(app1: SplitScreenHelper?, app2: SplitScreenHelper?) {
+            wmHelper.waitFor("primaryAndSecondaryAppsVisible") { dump ->
+                val primaryAppVisible = app1?.let {
+                    dump.wmState.isWindowSurfaceShown(app1.defaultWindowName)
+                } ?: false
+                val secondaryAppVisible = app2?.let {
+                    dump.wmState.isWindowSurfaceShown(app2.defaultWindowName)
+                } ?: false
+                primaryAppVisible && secondaryAppVisible
+            }
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 4fe69ad..f15044e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -20,6 +20,7 @@
 import android.content.ComponentName
 import android.content.pm.PackageManager.FEATURE_LEANBACK
 import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import android.os.SystemProperties
 import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.util.Log
 import androidx.test.uiautomator.By
@@ -60,6 +61,9 @@
     companion object {
         private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
 
+        fun isShellTransitionsEnabled() =
+                SystemProperties.getBoolean("persist.debug.shell_transit", false)
+
         fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
             try {
                 SystemUtil.runShellCommand(instrumentation, cmd)
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
index cac46fe..086e8b7 100644
--- 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
@@ -61,7 +61,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+            require(wmHelper.waitImeShown()) { "IME did not appear" }
         }
     }
 
@@ -78,7 +78,7 @@
             if (wmHelper == null) {
                 uiDevice.waitForIdle()
             } else {
-                require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+                require(wmHelper.waitImeGone()) { "IME did did not close" }
             }
         } else {
             // While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index f4dd7de..d4b4e5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -62,7 +62,7 @@
         stringExtras: Map<String, String>
     ) {
         super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitFor { it.wmState.hasPipWindow() }
+        wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
     }
 
     private fun focusOnObject(selector: BySelector): Boolean {
@@ -84,7 +84,7 @@
         clickObject(ENTER_PIP_BUTTON_ID)
 
         // Wait on WMHelper or simply wait for 3 seconds
-        wmHelper?.waitFor { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+        wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
     }
 
     fun clickStartMediaSessionButton() {
@@ -137,7 +137,7 @@
         }
 
         // Wait for animation to complete.
-        wmHelper.waitFor { !it.wmState.hasPipWindow() }
+        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
         wmHelper.waitForHomeActivityVisible()
     }
 
@@ -167,7 +167,7 @@
         val windowRect = windowRegion.bounds
         uiDevice.click(windowRect.centerX(), windowRect.centerY())
         uiDevice.click(windowRect.centerX(), windowRect.centerY())
-        wmHelper.waitFor { !it.wmState.hasPipWindow() }
+        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
         wmHelper.waitForAppTransitionIdle()
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 4f12f2b..508e939 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,22 +16,24 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -60,16 +62,16 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
-            splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
+            splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
@@ -77,27 +79,39 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
         }
     }
 
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
                 repetitions = SplitScreenHelper.TEST_REPETITIONS,
-                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+                supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910
+                supportedNavigationModes = listOf(
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
             )
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index f91f634..12f3909 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -25,7 +26,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -61,24 +62,34 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 85ded8a..ac85c48 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,17 +24,16 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -62,22 +62,22 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
-            secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+            secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
     @Presubmit
     @Test
@@ -85,15 +85,35 @@
 
     @Presubmit
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(secondaryApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index e958bf3..964af23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.canSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,12 +71,12 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-            nonResizeableApp.defaultWindowName,
-            splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+            nonResizeableApp.component,
+            splitScreenApp.component)
 
     @Before
     override fun setup() {
@@ -91,7 +92,12 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index d3acc82..1b8afa6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -67,12 +68,12 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                nonResizeableApp.defaultWindowName,
-                splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                nonResizeableApp.component,
+                splitScreenApp.component)
 
     @Before
     override fun setup() {
@@ -88,16 +89,21 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index bad4683..247965f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Presubmit
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -24,15 +25,13 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -67,31 +66,52 @@
                 }
             }
             transitions {
-                device.exitSplitScreenFromBottom()
+                device.exitSplitScreenFromBottom(wmHelper)
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            splitScreenApp.component, secondaryApp.component,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
+                    .then()
+                    .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
+        }
+    }
 
     @FlakyTest
     @Test
-    fun appWindowBecomesInVisible() =
-        testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesInVisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(secondaryApp.component)
+                    .then()
+                    .isAppWindowInvisible(secondaryApp.component)
+        }
+    }
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @FlakyTest
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 76dcd8b..af99fc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,15 +26,13 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -71,31 +71,52 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
-
-    @FlakyTest(bugId = 175687842)
-    @Test
-    fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
-
-    @FlakyTest
-    @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
-
-    @FlakyTest
-    @Test
-    fun appWindowBecomesInVisible() =
-        testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            splitScreenApp.component, secondaryApp.component,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+    @FlakyTest
+    @Test
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun appWindowBecomesInVisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index d0a64b3..95e4085 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,15 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -72,11 +69,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
-            nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                nonResizeableApp.component, splitScreenApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -92,44 +89,110 @@
 
     @Presubmit
     @Test
-    fun resizableAppLayerBecomesInvisible() =
-            testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+    fun resizableAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    .isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
+
+    /**
+     * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes
+     * invisible, it remains invisible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun resizableAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            // when the activity gets PAUSED the window may still be marked as visible
+            // it will be updated in the next log entry. This occurs because we record 1x
+            // per frame, thus ignore activity check here
+            this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+        }
+    }
+
+    /**
+     * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+     * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+     * visible, it remains visible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    // we log once per frame, upon logging, window may be visible or not depending
+                    // on what was processed until that moment. Both behaviors are correct
+                    .isAppWindowInvisible(nonResizeableApp.component,
+                            ignoreActivity = true, isOptional = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+        }
+    }
+
+    /**
+     * Asserts that both the app window and the activity are visible at the end of the trace
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisibleAtEnd() {
+        testSpec.assertWmEnd {
+            this.isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun resizableAppWindowBecomesInvisible() =
-            testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun nonResizableAppWindowBecomesVisible() =
-            testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
 
     @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isInvisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isInvisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
-                repetitions = SplitScreenHelper.TEST_REPETITIONS,
-                supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
+                    repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                    supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index c26c05f..65346aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,13 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,11 +69,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
-            nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+            nonResizeableApp.component, splitScreenApp.component,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -90,27 +89,60 @@
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
+
+    /**
+     * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+     * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+     * visible, it remains visible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    // we log once per frame, upon logging, window may be visible or not depending
+                    // on what was processed until that moment. Both behaviors are correct
+                    .isAppWindowInvisible(nonResizeableApp.component,
+                            ignoreActivity = true, isOptional = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-            testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index fb17589..547341a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,16 +25,12 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -73,11 +71,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
-                splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -93,37 +91,73 @@
 
     @Presubmit
     @Test
-    fun resizableAppLayerBecomesInvisible() =
-            testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+    fun resizableAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun resizableAppWindowBecomesInvisible() =
-        testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+    fun resizableAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            // when the activity gets PAUSED the window may still be marked as visible
+            // it will be updated in the next log entry. This occurs because we record 1x
+            // per frame, thus ignore activity check here
+            this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(nonResizeableApp.component)
+                    .then()
+                    .isAppWindowVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
 
     @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isInvisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isInvisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index a9c28ef..3f86658 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,14 +24,12 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -71,11 +70,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
-                splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -91,27 +90,60 @@
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(nonResizeableApp.component, isOptional = true)
+                    .then()
+                    // if the window reappears after re-parenting it will most likely not
+                    // be visible in the first log entry (because we log only 1x per frame)
+                    .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index a4d2ab51..7b4b71b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,10 +16,10 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -27,20 +27,18 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
 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.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
@@ -62,8 +60,6 @@
 class LegacySplitScreenToLauncher(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
-        .launcherStrategy.supportedLauncherPackage
     private val testApp = SimpleAppHelper(instrumentation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
@@ -90,25 +86,25 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
 
     @Presubmit
     @Test
@@ -122,19 +118,39 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
-    @Presubmit
+    @Postsubmit
     @Test
     fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
 
+    @Postsubmit
+    @Test
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun focusDoesNotChange() {
+        testSpec.assertEventLog {
+            this.focusDoesNotChange()
+        }
+    }
+
     @Presubmit
     @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage())
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @FlakyTest(bugId = 151179149)
+    @Presubmit
     @Test
-    fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index e8d4d1e..666d259 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.legacysplitscreen
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.content.Context
 import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
@@ -32,10 +33,12 @@
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.Test
 
@@ -46,12 +49,15 @@
     protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
     protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
     protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
-    protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
-        .launcherStrategy.supportedLauncherPackage
+    protected val LAUNCHER_COMPONENT = ComponentName("",
+            LauncherStrategyFactory.getInstance(instrumentation)
+                    .launcherStrategy.supportedLauncherPackage)
     private var prevDevEnableNonResizableMultiWindow = 0
 
     @Before
     open fun setup() {
+        // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+        assumeFalse(isShellTransitionsEnabled())
         prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
         if (prevDevEnableNonResizableMultiWindow != 0) {
             // Turn off the development option
@@ -70,8 +76,9 @@
      *
      * b/182720234
      */
-    open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-        WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    open val ignoredWindows: List<ComponentName> = listOf(
+        WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+        WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
@@ -138,9 +145,9 @@
     }
 
     companion object {
-        internal const val LIVE_WALLPAPER_PACKAGE_NAME =
-            "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
-        internal const val LETTERBOX_NAME = "Letterbox"
-        internal const val TOAST_NAME = "Toast"
+        internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+            "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
+        internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
+        internal val TOAST_COMPONENT = ComponentName("", "Toast")
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 05eb5f4..ec0c73a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,14 +25,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -62,22 +60,28 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage())
-
-    @FlakyTest
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+    @Presubmit
+    @Test
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
@@ -85,12 +89,27 @@
 
     @FlakyTest
     @Test
-    fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage())
+    fun layerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(splitScreenApp.component)
+                    .then()
+                    .isVisible(splitScreenApp.component)
+        }
+    }
 
-    @FlakyTest(bugId = 151179149)
+    @Presubmit
     @Test
-    fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`,
-        "recents_animation_input_consumer", "NexusLauncherActivity")
+    fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges(splitScreenApp.`package`,
+                    "recents_animation_input_consumer", "NexusLauncherActivity")
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 3e83b63..d7f71a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -28,23 +28,23 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 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.navBarLayerIsVisible
 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.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import com.android.wm.shell.flicker.testapp.Components
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -101,16 +101,16 @@
         }
 
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest(bugId = 156223549)
     @Test
     fun topAppWindowIsAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(sSimpleActivity)
+            this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
         }
     }
 
@@ -118,18 +118,18 @@
     @Test
     fun bottomAppWindowIsAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(sImeActivity)
+            this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
         }
     }
 
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
 
     @Test
     fun navBarLayerRotatesAndScales() =
@@ -142,21 +142,21 @@
     @Test
     fun topAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(sSimpleActivity)
+            this.isVisible(Components.SimpleActivity.COMPONENT)
         }
     }
 
     @Test
     fun bottomAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(sImeActivity)
+            this.isVisible(Components.ImeActivity.COMPONENT)
         }
     }
 
     @Test
     fun dividerLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(DOCKED_STACK_DIVIDER)
+            this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
         }
     }
 
@@ -166,7 +166,7 @@
         testSpec.assertLayersStart {
             val displayBounds = WindowUtils.displayBounds
             val dividerBounds =
-                entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+                layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
 
             val topAppBounds = Region(0, 0, dividerBounds.right,
                 dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -174,8 +174,8 @@
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
-            visibleRegion("SimpleActivity").coversExactly(topAppBounds)
-            visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
+            visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+            visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
         }
     }
 
@@ -185,7 +185,7 @@
         testSpec.assertLayersStart {
             val displayBounds = WindowUtils.displayBounds
             val dividerBounds =
-                entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+                layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
 
             val topAppBounds = Region(0, 0, dividerBounds.right,
                 dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -194,8 +194,8 @@
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
 
-            visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
-            visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
+            visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+            visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
         }
     }
 
@@ -207,8 +207,6 @@
     }
 
     companion object {
-        private const val sSimpleActivity = "SimpleActivity"
-        private const val sImeActivity = "ImeActivity"
         private val startRatio = Rational(1, 3)
         private val stopRatio = Rational(2, 3)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 58482ea..8a2b55b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -24,18 +24,17 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -66,21 +65,21 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -88,16 +87,26 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 06828d6..b325157 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -24,18 +24,17 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -66,35 +65,45 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
-        testSpec.config.startRotation, splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
+        testSpec.config.startRotation, splitScreenApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index f8e32bf..2be6936 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -18,26 +18,24 @@
 
 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.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -69,42 +67,66 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(secondaryApp.component, isOptional = true)
+                    .then()
+                    // if the window reappears after re-parenting it will most likely not
+                    // be visible in the first log entry (because we log only 1x per frame)
+                    .isAppWindowInvisible(secondaryApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index cb246ca..5782f14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -24,20 +24,19 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -74,27 +73,27 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -102,16 +101,31 @@
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(secondaryApp.component)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 2a66074..443204c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open 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,4 +16,4 @@
 
 package com.android.wm.shell.flicker.pip
 
-internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
+internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 00e50e7..39e89fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -62,18 +63,21 @@
     @Test
     fun pipAppRemainInsideVisibleBounds() {
         testSpec.assertWm {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
-    @Presubmit
+    @Postsubmit
     @Test
     fun showBothAppWindowsThenHidePip() {
         testSpec.assertWm {
-            showsAppWindow(testApp.defaultWindowName)
-                .showsAppWindowOnTop(pipApp.defaultWindowName)
+            // when the activity is STOPPING, sometimes it becomes invisible in an entry before
+            // the window, sometimes in the same entry. This occurs because we log 1x per frame
+            // thus we ignore activity here
+            isAppWindowVisible(testApp.component, ignoreActivity = true)
+                .isAppWindowOnTop(pipApp.component)
                 .then()
-                .hidesAppWindow(testApp.defaultWindowName)
+                .isAppWindowInvisible(testApp.component)
         }
     }
 
@@ -81,10 +85,10 @@
     @Test
     fun showBothAppLayersThenHidePip() {
         testSpec.assertLayers {
-            isVisible(testApp.defaultWindowName)
-                .isVisible(pipApp.defaultWindowName)
+            isVisible(testApp.component)
+                .isVisible(pipApp.component)
                 .then()
-                .isInvisible(testApp.defaultWindowName)
+                .isInvisible(testApp.component)
         }
     }
 
@@ -92,8 +96,8 @@
     @Test
     fun testAppCoversFullScreenWithPipOnDisplay() {
         testSpec.assertLayersStart {
-            visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(testApp.component).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversAtMost(displayBounds)
         }
     }
 
@@ -101,7 +105,7 @@
     @Test
     fun pipAppCoversFullScreen() {
         testSpec.assertLayersEnd {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversExactly(displayBounds)
         }
     }
 
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 b6af260..0f0a4ab 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
@@ -44,30 +44,24 @@
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
             transitions {
-                pipApp.clickEnterPipButton()
+                pipApp.clickEnterPipButton(wmHelper)
                 pipApp.expandPipWindow(wmHelper)
             }
         }
 
-    @FlakyTest
-    @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
-    }
-
     @Presubmit
     @Test
     fun pipAppWindowAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(pipApp.defaultWindowName)
+            this.isAppWindowVisible(pipApp.component)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun pipLayerBecomesVisible() {
+    fun pipAppLayerAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(pipApp.windowName)
+            this.isVisible(pipApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 3a1456e..67ad322 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -92,15 +92,13 @@
 
     @FlakyTest
     @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
-    }
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
     fun pipAppWindowIsAlwaysOnTop() {
         testSpec.assertWm {
-            showsAppWindowOnTop(pipApp.defaultWindowName)
+            isAppWindowOnTop(pipApp.component)
         }
     }
 
@@ -108,7 +106,7 @@
     @Test
     fun pipAppHidesTestApp() {
         testSpec.assertWmStart {
-            isInvisible(testApp.defaultWindowName)
+            isInvisible(testApp.component)
         }
     }
 
@@ -116,7 +114,7 @@
     @Test
     fun testAppWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(testApp.defaultWindowName)
+            isVisible(testApp.component)
         }
     }
 
@@ -124,8 +122,8 @@
     @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
-            isInvisible(testApp.defaultWindowName)
+            visibleRegion(pipApp.component).coversExactly(startingBounds)
+            isInvisible(testApp.component)
         }
     }
 
@@ -133,7 +131,7 @@
     @Test
     fun testAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
+            visibleRegion(testApp.component).coversExactly(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
deleted file mode 100644
index 0037059..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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 com.android.server.wm.traces.common.windowmanager.WindowManagerState
-import com.android.server.wm.traces.parser.toWindowName
-
-/**
- * Checks that an activity [activity] is in PIP mode
- */
-fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
-    val windowName = activity.toWindowName()
-    return isInPipMode(windowName)
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index eae7e97..28b1028 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -21,8 +21,8 @@
 import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import org.junit.Test
@@ -47,9 +47,9 @@
     @Test
     open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
-            this.showsAppWindow(PIP_WINDOW_TITLE)
+            this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
                 .then()
-                .hidesAppWindow(PIP_WINDOW_TITLE)
+                .isAppWindowInvisible(pipApp.component)
         }
     }
 
@@ -57,15 +57,21 @@
     @Test
     open fun pipLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.isVisible(PIP_WINDOW_TITLE)
+            this.isVisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
                 .then()
-                .isInvisible(PIP_WINDOW_TITLE)
+                .isInvisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
         }
     }
 
     @FlakyTest(bugId = 151179149)
     @Test
-    open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+    open fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
index cf84a2c..1c5d77f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
@@ -48,13 +48,9 @@
 
     @FlakyTest
     @Test
-    override fun pipLayerBecomesInvisible() {
-        super.pipLayerBecomesInvisible()
-    }
+    override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
 
     @FlakyTest
     @Test
-    override fun pipWindowBecomesInvisible() {
-        super.pipWindowBecomesInvisible()
-    }
+    override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index 524a1b4..356ec94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -56,19 +56,19 @@
 
     @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
@@ -85,7 +85,7 @@
 
     @Presubmit
     @Test
-    override fun noUncoveredRegions() = super.noUncoveredRegions()
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
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
index d88f94d..5719413 100644
--- 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
@@ -27,7 +27,7 @@
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -79,7 +79,7 @@
     fun pipInVisibleBounds() {
         testSpec.assertWm {
             val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -90,7 +90,7 @@
     @Test
     fun pipIsAboveAppWindow() {
         testSpec.assertWmTag(TAG_IME_VISIBLE) {
-            isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+            isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 6833b96..c09fdc2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -46,7 +46,6 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 161435597)
 @Group3
 class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
@@ -80,11 +79,11 @@
             }
         }
 
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
     fun pipWindowInsideDisplayBounds() {
         testSpec.assertWm {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -92,25 +91,17 @@
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(testApp.defaultWindowName)
-            isVisible(imeApp.defaultWindowName)
-            noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName)
+            isVisible(testApp.component)
+            isVisible(imeApp.component)
+            noWindowsOverlap(testApp.component, imeApp.component)
         }
     }
 
-    @Presubmit
-    @Test
-    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
     fun pipLayerInsideDisplayBounds() {
         testSpec.assertLayers {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -118,18 +109,14 @@
     @Test
     fun bothAppLayersVisible() {
         testSpec.assertLayersEnd {
-            visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
-            visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(testApp.component).coversAtMost(displayBounds)
+            visibleRegion(imeApp.component).coversAtMost(displayBounds)
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
-    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     companion object {
         const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index d531af2..e0ec757 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -26,10 +26,10 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
@@ -73,9 +73,9 @@
             }
         }
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
-    override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
     @FlakyTest
@@ -90,21 +90,21 @@
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
     fun appLayerRotates_StartingBounds() {
         testSpec.assertLayersStart {
-            visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+            visibleRegion(fixedApp.component).coversExactly(startingBounds)
+            visibleRegion(pipApp.component).coversAtMost(startingBounds)
         }
     }
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
     fun appLayerRotates_EndingBounds() {
         testSpec.assertLayersEnd {
-            visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
+            visibleRegion(fixedApp.component).coversExactly(endingBounds)
+            visibleRegion(pipApp.component).coversAtMost(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
index 1294ac9..914bc8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
@@ -63,13 +63,13 @@
 
     @Presubmit
     @Test
-    fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
+    fun pipAlwaysVisible() = testSpec.assertWm { this.isAppWindowVisible(pipApp.component) }
 
     @Presubmit
     @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(pipApp.component).coversAtMost(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 55e5c41..5abcf39 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -22,9 +22,9 @@
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import org.junit.FixMethodOrder
@@ -64,9 +64,11 @@
     @Test
     fun appReplacesPipWindow() {
         testSpec.assertWm {
-            this.showsAppWindow(PIP_WINDOW_TITLE)
+            this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
+                .isAppWindowOnTop(pipApp.component)
                 .then()
-                .showsAppWindowOnTop(pipApp.launcherName)
+                .invoke("hasNotPipWindow") { it.isNotPinned(pipApp.component) }
+                .isAppWindowOnTop(pipApp.component)
         }
     }
 
@@ -74,9 +76,11 @@
     @Test
     fun appReplacesPipLayer() {
         testSpec.assertLayers {
-            this.isVisible(PIP_WINDOW_TITLE)
+            this.isVisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
                 .then()
-                .isVisible(pipApp.launcherName)
+                .isVisible(pipApp.component)
+                .isInvisible(LAUNCHER_COMPONENT)
         }
     }
 
@@ -84,22 +88,26 @@
     @Test
     fun testAppCoversFullScreen() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversExactly(displayBounds)
         }
     }
 
     @FlakyTest(bugId = 151179149)
     @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity",
-        pipApp.launcherName, "NexusLauncherActivity")
+    fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges("NexusLauncherActivity",
+                    pipApp.launcherName, "NexusLauncherActivity")
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
-                    repetitions = 5)
+                    .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+                            repetitions = 5)
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b4c75a6..ca80d18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,25 +20,24 @@
 import android.content.Intent
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.isRotated
 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.navBarLayerIsVisible
 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.navBarWindowIsVisible
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.wm.shell.flicker.testapp.Components
 import org.junit.Test
@@ -162,19 +161,19 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
@@ -188,6 +187,6 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    open fun entireScreenCovered() =
+        testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1f58bb2..e7b6197 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -83,54 +82,70 @@
 
     @FlakyTest
     @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     @FlakyTest
     @Test
     override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
-    @Presubmit
+    @FlakyTest
     @Test
     fun pipWindowInsideDisplay() {
         testSpec.assertWmStart {
-            frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipAppShowsOnTop() {
-        testSpec.assertWmEnd {
-            showsAppWindowOnTop(pipApp.defaultWindowName)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipLayerInsideDisplay() {
-        testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipAlwaysVisible() = testSpec.assertWm {
-        this.showsAppWindow(pipApp.windowName)
-    }
-
-    @Presubmit
-    @Test
-    fun pipAppLayerCoversFullScreen() {
-        testSpec.assertLayersEnd {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
+            frameRegion(pipApp.component).coversAtMost(startingBounds)
         }
     }
 
     @FlakyTest
     @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
+    fun pipAppShowsOnTop() {
+        testSpec.assertWmEnd {
+            isAppWindowOnTop(pipApp.component)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun pipLayerInsideDisplay() {
+        testSpec.assertLayersStart {
+            visibleRegion(pipApp.component).coversAtMost(startingBounds)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm {
+        this.isAppWindowVisible(pipApp.component)
+    }
+
+    @FlakyTest
+    @Test
+    fun pipAppLayerCoversFullScreen() {
+        testSpec.assertLayersEnd {
+            visibleRegion(pipApp.component).coversExactly(endingBounds)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    override fun entireScreenCovered() {
+        super.entireScreenCovered()
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 0110ba3..061218a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -37,14 +37,17 @@
     private val systemUiResources =
             packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
     private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
-        val bounds = getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+        val bounds = getString(getIdentifier("pip_menu_bounds", "string",
+                SYSTEM_UI_PACKAGE_NAME))
         Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
     }
     private val playButtonDescription = systemUiResources.run {
-        getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
+        getString(getIdentifier("pip_play", "string",
+                SYSTEM_UI_PACKAGE_NAME))
     }
     private val pauseButtonDescription = systemUiResources.run {
-        getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME))
+        getString(getIdentifier("pip_pause", "string",
+                SYSTEM_UI_PACKAGE_NAME))
     }
 
     @Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 1b73920..1c66340 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -70,7 +70,8 @@
     // descendant and then retrieve the element from the menu and return to the caller of this
     // method.
     val elementSelector = By.desc(desc)
-    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR)
+            .hasDescendant(elementSelector)
 
     return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
             ?.findObject(elementSelector)
@@ -94,7 +95,8 @@
 }
 
 fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
-    focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+    focusOnAndClickTvPipMenuElement(By.desc(desc)
+            .pkg(SYSTEM_UI_PACKAGE_NAME)) ||
             error("Could not focus on the Pip menu object with \"$desc\" description")
     // So apparently Accessibility framework on TV is not very reliable and sometimes the state of
     // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 20ac5bf..1cbad15 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -47,6 +47,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
 
 import org.junit.After;
 import org.junit.Before;
@@ -71,6 +73,8 @@
     ShellTaskOrganizer mOrganizer;
     @Mock
     HandlerExecutor mExecutor;
+    @Mock
+    SyncTransactionQueue mSyncQueue;
 
     SurfaceSession mSession;
     SurfaceControl mLeash;
@@ -99,7 +103,14 @@
         }).when(mExecutor).execute(any());
 
         when(mOrganizer.getExecutor()).thenReturn(mExecutor);
-        mTaskView = new TaskView(mContext, mOrganizer);
+
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final TransactionRunnable r = invocationOnMock.getArgument(0);
+            r.runWithTransaction(new SurfaceControl.Transaction());
+            return null;
+        }).when(mSyncQueue).runInSync(any());
+
+        mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
         mTaskView.setListener(mExecutor, mViewListener);
     }
 
@@ -112,7 +123,7 @@
 
     @Test
     public void testSetPendingListener_throwsException() {
-        TaskView taskView = new TaskView(mContext, mOrganizer);
+        TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
         taskView.setListener(mExecutor, mViewListener);
         try {
             taskView.setListener(mExecutor, mViewListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 952dc31..e138595 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -61,7 +61,7 @@
         mSplitLayout = new SplitLayout(
                 "TestSplitLayout",
                 mContext,
-                getConfiguration(false),
+                getConfiguration(),
                 mSplitLayoutHandler,
                 b -> b.setParent(mRootLeash),
                 mDisplayImeController,
@@ -71,9 +71,22 @@
     @Test
     @UiThreadTest
     public void testUpdateConfiguration() {
-        mSplitLayout.init();
-        assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse();
-        assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue();
+        final Configuration config = getConfiguration();
+
+        // Verify it returns true if new config won't affect split layout.
+        assertThat(mSplitLayout.updateConfiguration(config)).isFalse();
+
+        // Verify updateConfiguration returns true if the orientation changed.
+        config.orientation = ORIENTATION_LANDSCAPE;
+        assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+        // Verify updateConfiguration returns true if it rotated.
+        config.windowConfiguration.setRotation(1);
+        assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+        // Verify updateConfiguration returns true if the root bounds changed.
+        config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
+        assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
     }
 
     @Test
@@ -108,12 +121,13 @@
         verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
     }
 
-    private static Configuration getConfiguration(boolean isLandscape) {
+    private static Configuration getConfiguration() {
         final Configuration configuration = new Configuration();
         configuration.unset();
-        configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+        configuration.orientation = ORIENTATION_PORTRAIT;
+        configuration.windowConfiguration.setRotation(0);
         configuration.windowConfiguration.setBounds(
-                new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+                new Rect(0, 0, 1080, 2160));
         return configuration;
     }
 
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
index 9d7c82b..0270093 100644
--- 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
@@ -79,6 +79,7 @@
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
     private TestShellExecutor mMainExecutor;
     private PipBoundsState mPipBoundsState;
+    private PipTransitionState mPipTransitionState;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
 
     private ComponentName mComponent1;
@@ -90,11 +91,12 @@
         mComponent1 = new ComponentName(mContext, "component1");
         mComponent2 = new ComponentName(mContext, "component2");
         mPipBoundsState = new PipBoundsState(mContext);
+        mPipTransitionState = new PipTransitionState();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
                 new PipSnapAlgorithm());
         mMainExecutor = new TestShellExecutor();
         mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
-                mMockSyncTransactionQueue, mPipBoundsState,
+                mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController,
                 mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
                 mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 56a0056..69ead3a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
@@ -46,7 +47,7 @@
 /** Tests for {@link SideStage} */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SideStageTests {
+public class SideStageTests extends ShellTestCase {
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
     @Mock private SyncTransactionQueue mSyncQueue;
@@ -60,8 +61,8 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mRootTask = new TestRunningTaskInfoBuilder().build();
-        mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
-                mSurfaceSession);
+        mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
+                mSyncQueue, mSurfaceSession);
         mSideStage.onTaskAppeared(mRootTask, mRootLeash);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index aca80f3..b6da868 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -102,7 +102,7 @@
         mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
         mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
-        mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
+        mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
         mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
@@ -131,6 +131,7 @@
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertTrue(accepted);
 
@@ -168,6 +169,7 @@
         mSideStage.onTaskAppeared(newTask, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertFalse(accepted);
         assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -188,6 +190,7 @@
         mSideStage.onTaskVanished(newTask);
         accepted = mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertFalse(accepted);
         assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -223,6 +226,7 @@
         mSideStage.onTaskVanished(mSideChild);
         mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertFalse(mStageCoordinator.isSplitScreenVisible());
     }
@@ -244,6 +248,7 @@
         mSideStage.onTaskVanished(mSideChild);
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertTrue(accepted);
         assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -274,6 +279,7 @@
         mSideStage.onTaskVanished(mSideChild);
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         assertTrue(accepted);
         assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -298,6 +304,7 @@
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
                 mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
                 mock(Transitions.TransitionFinishCallback.class));
         mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction());
     }
@@ -335,10 +342,11 @@
 
         @Override
         public void startAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+                SurfaceControl.Transaction startTransaction,
+                IRemoteTransitionFinishedCallback finishCallback)
                 throws RemoteException {
             mCalled = true;
-            finishCallback.onTransitionFinished(mRemoteFinishWCT);
+            finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 90b5b37..1a30f16 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -21,11 +21,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
+import android.os.SystemProperties;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
@@ -52,6 +54,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public final class StageTaskListenerTests {
+    private static final boolean ENABLE_SHELL_TRANSITIONS =
+            SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
     @Mock private SyncTransactionQueue mSyncQueue;
@@ -93,6 +98,8 @@
 
     @Test
     public void testChildTaskAppeared() {
+        // With shell transitions, the transition manages status changes, so skip this test.
+        assumeFalse(ENABLE_SHELL_TRANSITIONS);
         final ActivityManager.RunningTaskInfo childTask =
                 new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
 
@@ -110,6 +117,8 @@
 
     @Test
     public void testTaskVanished() {
+        // With shell transitions, the transition manages status changes, so skip this test.
+        assumeFalse(ENABLE_SHELL_TRANSITIONS);
         final ActivityManager.RunningTaskInfo childTask =
                 new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
         mStageTaskListener.mRootTaskInfo = mRootTask;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 2d2ab2c..a2b1f64 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -127,11 +127,13 @@
         TestTransitionHandler testHandler = new TestTransitionHandler() {
             @Override
             public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                    @NonNull SurfaceControl.Transaction t,
+                    @NonNull SurfaceControl.Transaction startTransaction,
+                    @NonNull SurfaceControl.Transaction finishTransaction,
                     @NonNull Transitions.TransitionFinishCallback finishCallback) {
                 for (TransitionInfo.Change chg : info.getChanges()) {
                     if (chg.getMode() == TRANSIT_CHANGE) {
-                        return super.startAnimation(transition, info, t, finishCallback);
+                        return super.startAnimation(transition, info, startTransaction,
+                                finishTransaction, finishCallback);
                     }
                 }
                 return false;
@@ -211,7 +213,7 @@
                     SurfaceControl.Transaction t,
                     IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
                 remoteCalled[0] = true;
-                finishCallback.onTransitionFinished(remoteFinishWCT);
+                finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
             }
 
             @Override
@@ -285,7 +287,7 @@
                     SurfaceControl.Transaction t,
                     IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
                 remoteCalled[0] = true;
-                finishCallback.onTransitionFinished(null /* wct */);
+                finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
             }
 
             @Override
@@ -332,7 +334,7 @@
                     SurfaceControl.Transaction t,
                     IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
                 remoteCalled[0] = true;
-                finishCallback.onTransitionFinished(remoteFinishWCT);
+                finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
             }
 
             @Override
@@ -358,9 +360,11 @@
         oneShot.setTransition(transitToken);
         IBinder anotherToken = new Binder();
         assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0),
-                mock(SurfaceControl.Transaction.class), testFinish));
+                mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+                testFinish));
         assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
-                mock(SurfaceControl.Transaction.class), testFinish));
+                mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+                testFinish));
     }
 
     @Test
@@ -477,7 +481,8 @@
 
         @Override
         public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
-                @NonNull SurfaceControl.Transaction t,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
             mFinishes.add(finishCallback);
             return true;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b14ade9..47c47e0 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
 #include "utils/Color.h"
 #include "utils/MathUtils.h"
 
+#include <log/log.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -90,7 +92,7 @@
 void Layer::draw(SkCanvas* canvas) {
     GrRecordingContext* context = canvas->recordingContext();
     if (context == nullptr) {
-        SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+        ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
         return;
     }
     SkMatrix layerTransform = getTransform();
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b8fa55a..c804418 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -88,6 +88,8 @@
 
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
+DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
+
 bool Properties::load() {
     bool prevDebugLayersUpdates = debugLayersUpdates;
     bool prevDebugOverdraw = debugOverdraw;
@@ -141,6 +143,9 @@
 
     enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, false);
 
+    // call isDrawingEnabled to force loading of the property
+    isDrawingEnabled();
+
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
 
@@ -210,5 +215,19 @@
     sRenderPipelineType = type;
 }
 
+void Properties::setDrawingEnabled(bool newDrawingEnabled) {
+    drawingEnabled = newDrawingEnabled ? DrawingEnabled::On : DrawingEnabled::Off;
+    enableRTAnimations = newDrawingEnabled;
+}
+
+bool Properties::isDrawingEnabled() {
+    if (drawingEnabled == DrawingEnabled::NotInitialized) {
+        bool drawingEnabledProp = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true);
+        drawingEnabled = drawingEnabledProp ? DrawingEnabled::On : DrawingEnabled::Off;
+        enableRTAnimations = drawingEnabledProp;
+    }
+    return drawingEnabled == DrawingEnabled::On;
+}
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7df6e2c..7f9782b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -187,6 +187,12 @@
  */
 #define PROPERTY_WEBVIEW_OVERLAYS_ENABLED "debug.hwui.webview_overlays_enabled"
 
+/**
+ * Property for globally GL drawing state. Can be overridden per process with
+ * setDrawingEnabled.
+ */
+#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -208,6 +214,8 @@
     UniformScale  // Uniform scale stretch everywhere
 };
 
+enum class DrawingEnabled { NotInitialized, On, Off };
+
 /**
  * Renderthread-only singleton which manages several static rendering properties. Most of these
  * are driven by system properties which are queried once at initialization, and again if init()
@@ -301,6 +309,11 @@
         stretchEffectBehavior = behavior;
     }
 
+    // Represents if drawing is enabled. Should only be Off in headless testing environments
+    static DrawingEnabled drawingEnabled;
+    static bool isDrawingEnabled();
+    static void setDrawingEnabled(bool enable);
+
 private:
     static StretchEffectBehavior stretchEffectBehavior;
     static ProfileType sProfileType;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 442ae0f..c945f27 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "RecordingCanvas.h"
+#include <hwui/Paint.h>
 
 #include <GrRecordingContext.h>
 
@@ -495,7 +496,7 @@
 
     sp<VectorDrawableRoot> mRoot;
     SkRect mBounds;
-    SkPaint paint;
+    Paint paint;
     BitmapPalette palette;
 };
 
@@ -833,7 +834,8 @@
                 // TODO: We should be const. Or not. Or just use a different map
                 // Unclear, but this is the quick fix
                 const T* op = reinterpret_cast<const T*>(opRaw);
-                transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+                const SkPaint* paint = &op->paint;
+                transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
             };
         }
     else if
@@ -842,7 +844,8 @@
                 // TODO: We should be const. Or not. Or just use a different map
                 // Unclear, but this is the quick fix
                 const T* op = reinterpret_cast<const T*>(opRaw);
-                transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+                const SkPaint* paint = &op->paint;
+                transformPaint(transform, const_cast<SkPaint*>(paint));
             };
         }
     else {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d032e2b..d6b6e16 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -182,7 +182,7 @@
     return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
 }
 
-void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) {
+void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const Paint& paint) {
 
     while (mCanvas->getSaveCount() > restoreCount + 1) {
         this->restore();
@@ -439,13 +439,13 @@
     mCanvas->drawColor(color, mode);
 }
 
-void SkiaCanvas::onFilterPaint(SkPaint& paint) {
+void SkiaCanvas::onFilterPaint(Paint& paint) {
     if (mPaintFilter) {
-        mPaintFilter->filter(&paint);
+        mPaintFilter->filterFullPaint(&paint);
     }
 }
 
-void SkiaCanvas::drawPaint(const SkPaint& paint) {
+void SkiaCanvas::drawPaint(const Paint& paint) {
     mCanvas->drawPaint(filterPaint(paint));
 }
 
@@ -552,9 +552,8 @@
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     auto image = bitmap.makeImage();
-    applyLooper(paint, [&](const SkPaint& p) {
-        auto sampling = SkSamplingOptions(p.getFilterQuality());
-        mCanvas->drawImage(image, left, top, sampling, &p);
+    applyLooper(paint, [&](const Paint& p) {
+        mCanvas->drawImage(image, left, top, p.sampling(), &p);
     });
 }
 
@@ -562,9 +561,8 @@
     auto image = bitmap.makeImage();
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
-    applyLooper(paint, [&](const SkPaint& p) {
-        auto sampling = SkSamplingOptions(p.getFilterQuality());
-        mCanvas->drawImage(image, 0, 0, sampling, &p);
+    applyLooper(paint, [&](const Paint& p) {
+        mCanvas->drawImage(image, 0, 0, p.sampling(), &p);
     });
 }
 
@@ -575,18 +573,12 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    applyLooper(paint, [&](const SkPaint& p) {
-        auto sampling = SkSamplingOptions(p.getFilterQuality());
-        mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
+    applyLooper(paint, [&](const Paint& p) {
+        mCanvas->drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
                                SkCanvas::kFast_SrcRectConstraint);
     });
 }
 
-static SkFilterMode paintToFilter(const Paint* paint) {
-    return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
-                                            : SkFilterMode::kNearest;
-}
-
 void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
                                 const float* vertices, const int* colors, const Paint* paint) {
     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
@@ -668,13 +660,13 @@
     if (paint) {
         pnt = *paint;
     }
-    SkSamplingOptions sampling(paintToFilter(&pnt));
+    SkSamplingOptions sampling = pnt.sampling();
     pnt.setShader(image->makeShader(sampling));
 
     auto v = builder.detach();
-    applyLooper(&pnt, [&](const SkPaint& p) {
+    applyLooper(&pnt, [&](const Paint& p) {
         SkPaint copy(p);
-        auto s = SkSamplingOptions(p.getFilterQuality());
+        auto s = p.sampling();
         if (s != sampling) {
             // applyLooper changed the quality?
             copy.setShader(image->makeShader(s));
@@ -707,9 +699,8 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
     auto image = bitmap.makeImage();
-    applyLooper(paint, [&](const SkPaint& p) {
-        auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
-        mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
+    applyLooper(paint, [&](const Paint& p) {
+        mCanvas->drawImageLattice(image.get(), lattice, dst, p.filterMode(), &p);
     });
 }
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 438a40c..fd6bbdc 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -23,6 +23,7 @@
 #include "VectorDrawable.h"
 #include "hwui/Canvas.h"
 #include "hwui/Paint.h"
+#include "hwui/BlurDrawLooper.h"
 
 #include <SkCanvas.h>
 #include "pipeline/skia/AnimatedDrawables.h"
@@ -73,7 +74,7 @@
     virtual int save(SaveFlags::Flags flags) override;
     virtual void restore() override;
     virtual void restoreToCount(int saveCount) override;
-    virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
+    virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) override;
 
     virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) override;
@@ -99,7 +100,7 @@
     virtual SkCanvasState* captureCanvasState() const override;
 
     virtual void drawColor(int color, SkBlendMode mode) override;
-    virtual void drawPaint(const SkPaint& paint) override;
+    virtual void drawPaint(const Paint& paint) override;
 
     virtual void drawPoint(float x, float y, const Paint& paint) override;
     virtual void drawPoints(const float* points, int count, const Paint& paint) override;
@@ -167,10 +168,10 @@
                                   const Paint& paint, const SkPath& path, size_t start,
                                   size_t end) override;
 
-    void onFilterPaint(SkPaint& paint);
+    void onFilterPaint(Paint& paint);
 
-    SkPaint filterPaint(const SkPaint& src) {
-        SkPaint dst(src);
+    Paint filterPaint(const Paint& src) {
+        Paint dst(src);
         this->onFilterPaint(dst);
         return dst;
     }
@@ -179,21 +180,20 @@
     template <typename Proc>
     void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
         BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
-        const SkPaint* skpPtr = paint;
-        SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+        Paint pnt = paint ? *paint : Paint();
         if (preFilter) {
-            preFilter(skp);
+            preFilter(pnt);
         }
-        this->onFilterPaint(skp);
+        this->onFilterPaint(pnt);
         if (looper) {
-            looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+            looper->apply(pnt, [&](SkPoint offset, const Paint& modifiedPaint) {
                 mCanvas->save();
                 mCanvas->translate(offset.fX, offset.fY);
                 proc(modifiedPaint);
                 mCanvas->restore();
             });
         } else {
-            proc(skp);
+            proc(pnt);
         }
     }
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 55f434f..f116641 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -463,10 +463,10 @@
         mStagingCache.dirty = false;
     }
 
-    SkPaint skp;
+    Paint skp;
     getPaintFor(&skp, mStagingProperties);
     Paint paint;
-    paint.setFilterQuality(skp.getFilterQuality());
+    paint.setFilterBitmap(skp.isFilterBitmap());
     paint.setColorFilter(skp.refColorFilter());
     paint.setAlpha(skp.getAlpha());
     outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
@@ -476,9 +476,9 @@
                           mStagingProperties.getBounds().bottom(), &paint);
 }
 
-void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const {
+void Tree::getPaintFor(Paint* outPaint, const TreeProperties& prop) const {
     // HWUI always draws VD with bilinear filtering.
-    outPaint->setFilterQuality(kLow_SkFilterQuality);
+    outPaint->setFilterBitmap(true);
     if (prop.getColorFilter() != nullptr) {
         outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
     }
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index ac7d41e..30bb04a 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -648,7 +648,7 @@
      */
     void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
 
-    void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+    void getPaintFor(Paint* outPaint, const TreeProperties &props) const;
     BitmapPalette computePalette();
 
     void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 876f5c8..d08bc5c5 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -157,7 +157,6 @@
         lazyPaint.emplace();
         lazyPaint->setAlpha(mProperties.mAlpha);
         lazyPaint->setColorFilter(mProperties.mColorFilter);
-        lazyPaint->setFilterQuality(kLow_SkFilterQuality);
     }
 
     canvas->concat(matrix);
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 27a038d..270d24a 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -24,7 +24,7 @@
 
 BlurDrawLooper::~BlurDrawLooper() = default;
 
-SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
+SkPoint BlurDrawLooper::apply(Paint* paint) const {
     paint->setColor(mColor);
     if (mBlurSigma > 0) {
         paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
diff --git a/libs/hwui/hwui/BlurDrawLooper.h b/libs/hwui/hwui/BlurDrawLooper.h
index 7e6786f..09a4e0f 100644
--- a/libs/hwui/hwui/BlurDrawLooper.h
+++ b/libs/hwui/hwui/BlurDrawLooper.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
 #define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
 
-#include <SkPaint.h>
+#include <hwui/Paint.h>
 #include <SkRefCnt.h>
 
 class SkColorSpace;
@@ -30,10 +30,10 @@
 
     ~BlurDrawLooper() override;
 
-    // proc(SkPoint offset, const SkPaint& modifiedPaint)
+    // proc(SkPoint offset, const Paint& modifiedPaint)
     template <typename DrawProc>
-    void apply(const SkPaint& paint, DrawProc proc) const {
-        SkPaint p(paint);
+    void apply(const Paint& paint, DrawProc proc) const {
+        Paint p(paint);
         proc(this->apply(&p), p);  // draw the shadow
         proc({0, 0}, paint);       // draw the original (on top)
     }
@@ -43,7 +43,7 @@
     const float mBlurSigma;
     const SkPoint mOffset;
 
-    SkPoint apply(SkPaint* paint) const;
+    SkPoint apply(Paint* paint) const;
 
     BlurDrawLooper(SkColor4f, float, SkPoint);
 };
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 9023613..70a558b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -162,7 +162,7 @@
     virtual int save(SaveFlags::Flags flags) = 0;
     virtual void restore() = 0;
     virtual void restoreToCount(int saveCount) = 0;
-    virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
+    virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) = 0;
 
     virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) = 0;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) = 0;
@@ -197,7 +197,7 @@
     // Canvas draw operations
     // ----------------------------------------------------------------------------
     virtual void drawColor(int color, SkBlendMode mode) = 0;
-    virtual void drawPaint(const SkPaint& paint) = 0;
+    virtual void drawPaint(const Paint& paint) = 0;
 
     // Geometry
     virtual void drawPoint(float x, float y, const Paint& paint) = 0;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5d9fad5..fc542c8 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,7 +24,6 @@
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkEncodedOrigin.h>
-#include <SkFilterQuality.h>
 #include <SkPaint.h>
 
 #undef LOG_TAG
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index d9c9eee..4a8f3e1 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,13 +17,13 @@
 #ifndef ANDROID_GRAPHICS_PAINT_H_
 #define ANDROID_GRAPHICS_PAINT_H_
 
-#include "BlurDrawLooper.h"
 #include "Typeface.h"
 
 #include <cutils/compiler.h>
 
 #include <SkFont.h>
 #include <SkPaint.h>
+#include <SkSamplingOptions.h>
 #include <string>
 
 #include <minikin/FontFamily.h>
@@ -32,6 +32,8 @@
 
 namespace android {
 
+class BlurDrawLooper;
+
 class Paint : public SkPaint {
 public:
     // Default values for underlined and strikethrough text,
@@ -60,7 +62,7 @@
     const SkFont& getSkFont() const { return mFont; }
 
     BlurDrawLooper* getLooper() const { return mLooper.get(); }
-    void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
+    void setLooper(sk_sp<BlurDrawLooper> looper);
 
     // These shadow the methods on SkPaint, but we need to so we can keep related
     // attributes in-sync.
@@ -138,7 +140,15 @@
     void setDevKern(bool d) { mDevKern = d; }
 
     // Deprecated -- bitmapshaders will be taking this flag explicitly
-    bool isFilterBitmap() const { return this->getFilterQuality() != kNone_SkFilterQuality; }
+    bool isFilterBitmap() const { return mFilterBitmap; }
+    void setFilterBitmap(bool filter) { mFilterBitmap = filter; }
+
+    SkFilterMode filterMode() const {
+        return mFilterBitmap ? SkFilterMode::kLinear : SkFilterMode::kNearest;
+    }
+    SkSamplingOptions sampling() const {
+        return SkSamplingOptions(this->filterMode());
+    }
 
     // The Java flags (Paint.java) no longer fit into the native apis directly.
     // These methods handle converting to and from them and the native representations
@@ -169,6 +179,7 @@
     // nullptr is valid: it means the default typeface.
     const Typeface* mTypeface = nullptr;
     Align mAlign = kLeft_Align;
+    bool mFilterBitmap = false;
     bool mStrikeThru = false;
     bool mUnderline = false;
     bool mDevKern = false;
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
index 0e7b619..4996aa4 100644
--- a/libs/hwui/hwui/PaintFilter.h
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -1,17 +1,18 @@
 #ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
 #define ANDROID_GRAPHICS_PAINT_FILTER_H_
 
-class SkPaint;
+#include <SkRefCnt.h>
 
 namespace android {
 
+class Paint;
+
 class PaintFilter : public SkRefCnt {
 public:
     /**
      *  Called with the paint that will be used to draw.
      *  The implementation may modify the paint as they wish.
      */
-    virtual void filter(SkPaint*) = 0;
     virtual void filterFullPaint(Paint*) = 0;
 };
 
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index fa2674f..aac928f 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "Paint.h"
+#include "BlurDrawLooper.h"
 
 namespace android {
 
@@ -43,6 +44,7 @@
         , mHyphenEdit(paint.mHyphenEdit)
         , mTypeface(paint.mTypeface)
         , mAlign(paint.mAlign)
+        , mFilterBitmap(paint.mFilterBitmap)
         , mStrikeThru(paint.mStrikeThru)
         , mUnderline(paint.mUnderline)
         , mDevKern(paint.mDevKern) {}
@@ -62,6 +64,7 @@
     mHyphenEdit = other.mHyphenEdit;
     mTypeface = other.mTypeface;
     mAlign = other.mAlign;
+    mFilterBitmap = other.mFilterBitmap;
     mStrikeThru = other.mStrikeThru;
     mUnderline = other.mUnderline;
     mDevKern = other.mDevKern;
@@ -77,6 +80,7 @@
            a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
            a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
            a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
+           a.mFilterBitmap == b.mFilterBitmap &&
            a.mStrikeThru == b.mStrikeThru && a.mUnderline == b.mUnderline &&
            a.mDevKern == b.mDevKern;
 }
@@ -88,11 +92,16 @@
     mFont.setEdging(SkFont::Edging::kAlias);
     mLooper.reset();
 
+    mFilterBitmap = false;
     mStrikeThru = false;
     mUnderline = false;
     mDevKern = false;
 }
 
+void Paint::setLooper(sk_sp<BlurDrawLooper> looper) {
+    mLooper = std::move(looper);
+}
+
 void Paint::setAntiAlias(bool aa) {
     // Java does not support/understand subpixel(lcd) antialiasing
     SkASSERT(mFont.getEdging() != SkFont::Edging::kSubpixelAntiAlias);
@@ -131,9 +140,6 @@
     uint32_t flags = 0;
     flags |= -(int)paint.isAntiAlias() & sAntiAliasFlag;
     flags |= -(int)paint.isDither()    & sDitherFlag;
-    if (paint.getFilterQuality() != kNone_SkFilterQuality) {
-        flags |= sFilterBitmapFlag;
-    }
     return flags;
 }
 
@@ -150,12 +156,6 @@
 static void applyLegacyFlagsToPaint(uint32_t flags, SkPaint* paint) {
     paint->setAntiAlias((flags & sAntiAliasFlag) != 0);
     paint->setDither   ((flags & sDitherFlag) != 0);
-
-    if (flags & sFilterBitmapFlag) {
-        paint->setFilterQuality(kLow_SkFilterQuality);
-    } else {
-        paint->setFilterQuality(kNone_SkFilterQuality);
-    }
 }
 
 static void applyLegacyFlagsToFont(uint32_t flags, SkFont* font) {
@@ -182,18 +182,20 @@
 
 uint32_t Paint::getJavaFlags() const {
     uint32_t flags = paintToLegacyFlags(*this) | fontToLegacyFlags(mFont);
-    flags |= -(int)mStrikeThru & sStrikeThruFlag;
-    flags |= -(int)mUnderline  & sUnderlineFlag;
-    flags |= -(int)mDevKern    & sDevKernFlag;
+    flags |= -(int)mStrikeThru   & sStrikeThruFlag;
+    flags |= -(int)mUnderline    & sUnderlineFlag;
+    flags |= -(int)mDevKern      & sDevKernFlag;
+    flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
     return flags;
 }
 
 void Paint::setJavaFlags(uint32_t flags) {
     applyLegacyFlagsToPaint(flags, this);
     applyLegacyFlagsToFont(flags, &mFont);
-    mStrikeThru = (flags & sStrikeThruFlag) != 0;
-    mUnderline  = (flags & sUnderlineFlag) != 0;
-    mDevKern    = (flags & sDevKernFlag) != 0;
+    mStrikeThru   = (flags & sStrikeThruFlag) != 0;
+    mUnderline    = (flags & sUnderlineFlag) != 0;
+    mDevKern      = (flags & sDevKernFlag) != 0;
+    mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
 }
 
 }  // namespace android
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 05278f2..6989ac0 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -873,7 +873,7 @@
                                      jlong bitmapHandle, jint density, jobject parcel) {
 #ifdef __ANDROID__ // Layoutlib does not support parcel
     if (parcel == NULL) {
-        SkDebugf("------- writeToParcel null parcel\n");
+        ALOGD("------- writeToParcel null parcel\n");
         return JNI_FALSE;
     }
 
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 785a5dc..15e529e 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -107,7 +107,7 @@
             jint n = env->CallIntMethod(fJavaInputStream,
                                         gInputStream_readMethodID, fJavaByteArray, 0, requested);
             if (checkException(env)) {
-                SkDebugf("---- read threw an exception\n");
+                ALOGD("---- read threw an exception\n");
                 return bytesRead;
             }
 
@@ -119,7 +119,7 @@
             env->GetByteArrayRegion(fJavaByteArray, 0, n,
                                     reinterpret_cast<jbyte*>(buffer));
             if (checkException(env)) {
-                SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
+                ALOGD("---- read:GetByteArrayRegion threw an exception\n");
                 return bytesRead;
             }
 
@@ -136,7 +136,7 @@
         jlong skipped = env->CallLongMethod(fJavaInputStream,
                                             gInputStream_skipMethodID, (jlong)size);
         if (checkException(env)) {
-            SkDebugf("------- skip threw an exception\n");
+            ALOGD("------- skip threw an exception\n");
             return 0;
         }
         if (skipped < 0) {
@@ -236,7 +236,7 @@
             if (env->ExceptionCheck()) {
                 env->ExceptionDescribe();
                 env->ExceptionClear();
-                SkDebugf("--- write:SetByteArrayElements threw an exception\n");
+                ALOGD("--- write:SetByteArrayElements threw an exception\n");
                 return false;
             }
 
@@ -245,7 +245,7 @@
             if (env->ExceptionCheck()) {
                 env->ExceptionDescribe();
                 env->ExceptionClear();
-                SkDebugf("------- write threw an exception\n");
+                ALOGD("------- write threw an exception\n");
                 return false;
             }
 
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index f84a4bd..c5c4711 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -15,6 +15,8 @@
 
 #include "gif_lib.h"
 
+#include <log/log.h>
+
 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
 #define DGifCloseFile(a, b) DGifCloseFile(a)
 #endif
@@ -244,7 +246,7 @@
     }
 
     if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
-        SkDEBUGFAIL("bad colortable setup");
+        ALOGD("bad colortable setup");
         return;
     }
 
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index bcec0fa..22a1e1f 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -663,8 +663,7 @@
     }
 
     static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
-        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
-                filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+        reinterpret_cast<Paint*>(paintHandle)->setFilterBitmap(filterBitmap);
     }
 
     static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index ec115b4..c30e29b 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -29,10 +29,6 @@
         fClearFlags = static_cast<uint16_t>(clearFlags);
         fSetFlags = static_cast<uint16_t>(setFlags);
     }
-    void filter(SkPaint* paint) override {
-        uint32_t flags = Paint::GetSkPaintJavaFlags(*paint);
-        Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags);
-    }
     void filterFullPaint(Paint* paint) override {
         paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags);
     }
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index ac2f5b7..106c6db 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -18,6 +18,7 @@
 #include "SkUtils.h"
 #include "SkData.h"
 
+#include <inttypes.h>
 #include <log/log.h>
 
 using namespace android;
@@ -30,7 +31,7 @@
 bool AssetStreamAdaptor::rewind() {
     off64_t pos = fAsset->seek(0, SEEK_SET);
     if (pos == (off64_t)-1) {
-        SkDebugf("----- fAsset->seek(rewind) failed\n");
+        ALOGD("----- fAsset->seek(rewind) failed\n");
         return false;
     }
     return true;
@@ -58,7 +59,7 @@
 size_t AssetStreamAdaptor::getPosition() const {
     const off64_t offset = fAsset->seek(0, SEEK_CUR);
     if (offset == -1) {
-        SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n");
+        ALOGD("---- fAsset->seek(0, SEEK_CUR) failed\n");
         return 0;
     }
 
@@ -67,7 +68,7 @@
 
 bool AssetStreamAdaptor::seek(size_t position) {
     if (fAsset->seek(position, SEEK_SET) == -1) {
-        SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n");
+        ALOGD("---- fAsset->seek(0, SEEK_SET) failed\n");
         return false;
     }
 
@@ -76,7 +77,7 @@
 
 bool AssetStreamAdaptor::move(long offset) {
     if (fAsset->seek(offset, SEEK_CUR) == -1) {
-        SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+        ALOGD("---- fAsset->seek(%li, SEEK_CUR) failed\n", offset);
         return false;
     }
 
@@ -95,12 +96,12 @@
 
         off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
         if (-1 == oldOffset) {
-            SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+            ALOGD("---- fAsset->seek(oldOffset) failed\n");
             return 0;
         }
         off64_t newOffset = fAsset->seek(size, SEEK_CUR);
         if (-1 == newOffset) {
-            SkDebugf("---- fAsset->seek(%d) failed\n", size);
+            ALOGD("---- fAsset->seek(%zu) failed\n", size);
             return 0;
         }
         amount = newOffset - oldOffset;
@@ -121,20 +122,20 @@
 
     const off64_t seekReturnVal = asset->seek(0, SEEK_SET);
     if ((off64_t)-1 == seekReturnVal) {
-        SkDebugf("---- copyAsset: asset rewind failed\n");
+        ALOGD("---- copyAsset: asset rewind failed\n");
         return NULL;
     }
 
     const off64_t size = asset->getLength();
     if (size <= 0) {
-        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
+        ALOGD("---- copyAsset: asset->getLength() returned %" PRId64 "\n", size);
         return NULL;
     }
 
     sk_sp<SkData> data(SkData::MakeUninitialized(size));
     const off64_t len = asset->read(data->writable_data(), size);
     if (len != size) {
-        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
+        ALOGD("---- copyAsset: asset->read(%" PRId64 ") returned %" PRId64 "\n", size, len);
         return NULL;
     }
 
@@ -143,7 +144,7 @@
 
 jobject android::nullObjectReturn(const char msg[]) {
     if (msg) {
-        SkDebugf("--- %s\n", msg);
+        ALOGD("--- %s\n", msg);
     }
     return NULL;
 }
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 689cf0b..77f42ae 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -85,7 +85,7 @@
 
 void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
         uint8_t* yuv, int* offsets) {
-    SkDebugf("onFlyCompress");
+    ALOGD("onFlyCompress");
     JSAMPROW y[16];
     JSAMPROW cb[8];
     JSAMPROW cr[8];
@@ -161,7 +161,7 @@
 
 void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
         uint8_t* yuv, int* offsets) {
-    SkDebugf("onFlyCompress_422");
+    ALOGD("onFlyCompress_422");
     JSAMPROW y[16];
     JSAMPROW cb[16];
     JSAMPROW cr[16];
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index a611f7c..a4cf028 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -197,30 +197,53 @@
 static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
 static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
 
-static SkClipOp opHandleToClipOp(jint opHandle) {
-    // The opHandle is defined in Canvas.java to be Region::Op
-    SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
-
-    // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
-    // this function can perform a range check and throw an unsupported-exception.
-    // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
-
-    // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
-    // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
-    return static_cast<SkClipOp>(rgnOp);
-}
-
 static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
                          jfloat r, jfloat b, jint opHandle) {
-    bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
-            opHandleToClipOp(opHandle));
+    // The opHandle is defined in Canvas.java to be Region::Op
+    SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+    bool nonEmptyClip;
+    switch (rgnOp) {
+        case SkRegion::Op::kReplace_Op:
+            // For now replace can still be handled as an SkClipOp but will be emulated in the
+            // future
+            [[fallthrough]];
+        case SkRegion::Op::kIntersect_Op:
+        case SkRegion::Op::kDifference_Op:
+            // Intersect and difference are supported clip operations
+            nonEmptyClip =
+                    get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
+            break;
+        default:
+            // All other operations would expand the clip and are no longer supported,
+            // so log and skip (to avoid breaking legacy apps).
+            ALOGW("Ignoring unsupported clip operation %d", opHandle);
+            SkRect clipBounds;  // ignored
+            nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+            break;
+    }
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
                          jint opHandle) {
+    SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
+    bool nonEmptyClip;
+    switch (rgnOp) {
+        case SkRegion::Op::kReplace_Op:
+            // For now replace can still be handled as an SkClipOp but will be emulated in the
+            // future
+            [[fallthrough]];
+        case SkRegion::Op::kIntersect_Op:
+        case SkRegion::Op::kDifference_Op:
+            nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, static_cast<SkClipOp>(rgnOp));
+            break;
+        default:
+            ALOGW("Ignoring unsupported clip operation %d", opHandle);
+            SkRect clipBounds;  // ignored
+            nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+            break;
+    }
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -233,7 +256,7 @@
         jlong colorLong, jint modeHandle) {
     SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
     sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
-    SkPaint p;
+    Paint p;
     p.setColor4f(color, cs.get());
 
     SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
@@ -421,7 +444,7 @@
         if (paint) {
             filteredPaint = *paint;
         }
-        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+        filteredPaint.setFilterBitmap(true);
 
         canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
                 &filteredPaint);
@@ -443,7 +466,7 @@
             if (paint) {
                 filteredPaint = *paint;
             }
-            filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+            filteredPaint.setFilterBitmap(true);
             canvas->drawBitmap(bitmap, left, top, &filteredPaint);
         } else {
             canvas->drawBitmap(bitmap, left, top, paint);
@@ -458,7 +481,7 @@
         if (paint) {
             filteredPaint = *paint;
         }
-        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+        filteredPaint.setFilterBitmap(true);
 
         canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
         canvas->restore();
@@ -486,7 +509,7 @@
         if (paint) {
             filteredPaint = *paint;
         }
-        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+        filteredPaint.setFilterBitmap(true);
         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
                            dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
     } else {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c4cdb7d..afe86cc 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -898,6 +898,14 @@
     DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
 }
 
+static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) {
+    Properties::setDrawingEnabled(enabled);
+}
+
+static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) {
+    return Properties::isDrawingEnabled();
+}
+
 // ----------------------------------------------------------------------------
 // HardwareRendererObserver
 // ----------------------------------------------------------------------------
@@ -1034,6 +1042,8 @@
         {"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
         {"isWebViewOverlaysEnabled", "()Z",
          (void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled},
+        {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled},
+        {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled},
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 471a7f7..e32788c 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -75,7 +75,7 @@
                               const SkRect* dstRect,
                               bool useLayerTransform) {
     if (context == nullptr) {
-        SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+        ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
         return false;
     }
     // transform the matrix based on the layer
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 76c4a03..9c51e62 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -187,28 +187,18 @@
 void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
     // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
     // older.
-    if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+    if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
         paint.setBlendMode(SkBlendMode::kDstOut);
     }
 }
 
-static SkFilterMode Paint_to_filter(const SkPaint& paint) {
-    return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
-                                                             : SkFilterMode::kNearest;
-}
-
-static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
-    // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
-    return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
-}
-
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     sk_sp<SkImage> image = bitmap.makeImage();
 
     applyLooper(
             paint,
-            [&](const SkPaint& p) {
-                mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+            [&](const Paint& p) {
+                mRecorder.drawImage(image, left, top, p.sampling(), &p, bitmap.palette());
             },
             FilterForImage);
 
@@ -228,8 +218,8 @@
 
     applyLooper(
             paint,
-            [&](const SkPaint& p) {
-                mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+            [&](const Paint& p) {
+                mRecorder.drawImage(image, 0, 0, p.sampling(), &p, bitmap.palette());
             },
             FilterForImage);
 
@@ -248,8 +238,8 @@
 
     applyLooper(
             paint,
-            [&](const SkPaint& p) {
-                mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+            [&](const Paint& p) {
+                mRecorder.drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
                                         SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
             },
             FilterForImage);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index ddfb66f..3c7617d 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -68,7 +68,7 @@
     ATRACE_CALL();
 
     if (canvas->recordingContext() == nullptr) {
-        SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
+        ALOGD("Attempting to draw VkInteropFunctor into an unsupported surface");
         return;
     }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 025be7b..ab9d0b2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -256,7 +256,7 @@
 }
 
 void CanvasContext::allocateBuffers() {
-    if (mNativeSurface) {
+    if (mNativeSurface && Properties::isDrawingEnabled()) {
         ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
     }
 }
@@ -480,7 +480,8 @@
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
 
-    if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+    if (!Properties::isDrawingEnabled() ||
+        (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         // Notify the callbacks, even if there's nothing to draw so they aren't waiting
         // indefinitely
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc3..2fed468 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -90,9 +90,17 @@
      *         and false otherwise (e.g. cache limits have been exceeded).
      */
     bool pinImages(std::vector<SkImage*>& mutableImages) {
+        if (!Properties::isDrawingEnabled()) {
+            return true;
+        }
         return mRenderPipeline->pinImages(mutableImages);
     }
-    bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); }
+    bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+        if (!Properties::isDrawingEnabled()) {
+            return true;
+        }
+        return mRenderPipeline->pinImages(images);
+    }
 
     /**
      * Unpin any image that had be previously pinned to the GPU cache
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 10ba079..31a8ae1 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -49,7 +49,7 @@
             paint.setAntiAlias(true);
             paint.setColor(Color::Green_700);
             canvas.drawCircle(200, 200, 200, paint);
-            SkPaint alphaPaint;
+            Paint alphaPaint;
             alphaPaint.setAlpha(128);
             canvas.restoreUnclippedLayer(unclippedSaveLayer, alphaPaint);
             canvas.restore();
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index a1ba70a..dc1b2e6 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -61,11 +61,11 @@
 TEST(SkiaBehavior, porterDuffCreateIsCached) {
     SkPaint paint;
     paint.setBlendMode(SkBlendMode::kOverlay);
-    auto expected = paint.getBlendMode();
+    auto expected = paint.asBlendMode();
     paint.setBlendMode(SkBlendMode::kClear);
-    ASSERT_NE(expected, paint.getBlendMode());
+    ASSERT_NE(expected, paint.asBlendMode());
     paint.setBlendMode(SkBlendMode::kOverlay);
-    ASSERT_EQ(expected, paint.getBlendMode());
+    ASSERT_EQ(expected, paint.asBlendMode());
 }
 
 TEST(SkiaBehavior, pathIntersection) {
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index a8f2d9a..94bcb11 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -32,13 +32,6 @@
  */
 class PaintUtils {
 public:
-    static inline GLenum getFilter(const SkPaint* paint) {
-        if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
-            return GL_LINEAR;
-        }
-        return GL_NEAREST;
-    }
-
     static bool isOpaquePaint(const SkPaint* paint) {
         if (!paint) return true;  // default (paintless) behavior is SrcOver, black
 
@@ -48,7 +41,7 @@
         }
 
         // Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
-        SkBlendMode mode = paint->getBlendMode();
+        const auto mode = paint->asBlendMode();
         return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
     }
 
@@ -59,7 +52,7 @@
     }
 
     static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
-        return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
+        return paint ? paint->getBlendMode_or(SkBlendMode::kSrcOver) : SkBlendMode::kSrcOver;
     }
 
     static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 0970c1c..73c5c82 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -53,7 +53,9 @@
      *
      * @return true if this request should be ignored while updating app ops with location usage
      *
+     * @hide
      */
+    @SystemApi
     public boolean isHiddenFromAppOps() {
         return mHiddenFromAppOps;
     }
@@ -65,9 +67,10 @@
      * Driving Assistance Systems) application.
      *
      * @return true if all limiting factors will be ignored to satisfy GNSS request
+     *
      * @hide
      */
-    // TODO: make this system api
+    @SystemApi
     public boolean isAdasGnssBypass() {
         return mAdasGnssBypass;
     }
@@ -78,7 +81,10 @@
      * possible limiting factors will be ignored in order to satisfy this last location request.
      *
      * @return true if all limiting factors will be ignored to satisfy this request
+     *
+     * @hide
      */
+    @SystemApi
     public boolean isLocationSettingsIgnored() {
         return mLocationSettingsIgnored;
     }
@@ -192,7 +198,10 @@
          *
          * <p>Permissions enforcement occurs when resulting last location request is actually used,
          * not when this method is invoked.
+         *
+         * @hide
          */
+        @SystemApi
         @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
         public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) {
             mHiddenFromAppOps = hiddenFromAppOps;
@@ -211,7 +220,7 @@
          *
          * @hide
          */
-        // TODO: make this system api
+        @SystemApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
             mAdasGnssBypass = adasGnssBypass;
@@ -226,7 +235,10 @@
          *
          * <p>Permissions enforcement occurs when resulting last location request is actually used,
          * not when this method is invoked.
+         *
+         * @hide
          */
+        @SystemApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
             mLocationSettingsIgnored = locationSettingsIgnored;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 526b84e..42bbc72 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -325,7 +325,7 @@
      *
      * @hide
      */
-    // TODO: @SystemApi
+    @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
             "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
@@ -338,7 +338,7 @@
      *
      * @hide
      */
-    // TODO: @SystemApi
+    @SystemApi
     public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
 
     /**
@@ -656,7 +656,7 @@
      *
      * @hide
      */
-    //TODO: @SystemApi
+    @SystemApi
     public boolean isAdasGnssLocationEnabled() {
         try {
             return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
@@ -673,7 +673,7 @@
      *
      * @hide
      */
-    // TODO: @SystemApi
+    @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setAdasGnssLocationEnabled(boolean enabled) {
         try {
@@ -1569,6 +1569,7 @@
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
     public boolean injectLocation(@NonNull Location location) {
         Preconditions.checkArgument(location != null, "invalid null location");
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index b48e596..d6e203c 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -660,7 +660,7 @@
      *
      * @hide
      */
-    // TODO: @SystemApi
+    @SystemApi
     public boolean isAdasGnssBypass() {
         return mAdasGnssBypass;
     }
@@ -1139,7 +1139,7 @@
          *
          * @hide
          */
-        // TODO: @SystemApi
+        @SystemApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
             mAdasGnssBypass = adasGnssBypass;
diff --git a/media/Android.bp b/media/Android.bp
index a66236e..cf4a0b1 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -8,23 +8,6 @@
 }
 
 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",
-        "aidl/android/media/audio/common/AudioFormat.aidl",
-        "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
-        "aidl/android/media/audio/common/AudioStreamType.aidl",
-        "aidl/android/media/audio/common/AudioUsage.aidl",
-    ],
-}
-
-aidl_interface {
     name: "media_permission-aidl",
     unstable: true,
     host_supported: true,
@@ -40,30 +23,92 @@
     name: "soundtrigger_middleware-aidl",
     unstable: true,
     local_include_dir: "aidl",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+    },
     srcs: [
-        "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
-        "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
         "aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
         "aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
         "aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
-        "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
-        "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
-        "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
-        "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
-        "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
-        "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
-        "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
-        "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
-        "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
-        "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
-        "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
-        "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
         "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
-        "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
-        "aidl/android/media/soundtrigger_middleware/Status.aidl",
     ],
     imports: [
-        "audio_common-aidl",
+        "android.media.soundtrigger.types",
         "media_permission-aidl",
     ],
 }
+
+aidl_interface {
+    name: "android.media.audio.common.types",
+    vendor_available: true,
+    host_supported: true,
+    double_loadable: true,
+    flags: ["-Werror", "-Weverything", ],
+    local_include_dir: "aidl",
+    srcs: [
+        "aidl/android/media/audio/common/AudioChannelMask.aidl",
+        "aidl/android/media/audio/common/AudioConfig.aidl",
+        "aidl/android/media/audio/common/AudioFormat.aidl",
+        "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+        "aidl/android/media/audio/common/AudioStreamType.aidl",
+        "aidl/android/media/audio/common/AudioUsage.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: true,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
+
+aidl_interface {
+    name: "android.media.soundtrigger.types",
+    vendor_available: true,
+    flags: ["-Werror", "-Weverything", ],
+    local_include_dir: "aidl",
+    srcs: [
+        "aidl/android/media/soundtrigger/AudioCapabilities.aidl",
+        "aidl/android/media/soundtrigger/ConfidenceLevel.aidl",
+        "aidl/android/media/soundtrigger/ModelParameter.aidl",
+        "aidl/android/media/soundtrigger/ModelParameterRange.aidl",
+        "aidl/android/media/soundtrigger/Phrase.aidl",
+        "aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl",
+        "aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl",
+        "aidl/android/media/soundtrigger/PhraseSoundModel.aidl",
+        "aidl/android/media/soundtrigger/Properties.aidl",
+        "aidl/android/media/soundtrigger/RecognitionConfig.aidl",
+        "aidl/android/media/soundtrigger/RecognitionEvent.aidl",
+        "aidl/android/media/soundtrigger/RecognitionMode.aidl",
+        "aidl/android/media/soundtrigger/RecognitionStatus.aidl",
+        "aidl/android/media/soundtrigger/SoundModel.aidl",
+        "aidl/android/media/soundtrigger/SoundModelType.aidl",
+        "aidl/android/media/soundtrigger/Status.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: true,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+    imports: [
+        "android.media.audio.common.types",
+    ],
+}
diff --git a/media/aidl/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
index b9b08e6..17be8dd 100644
--- a/media/aidl/android/media/audio/common/AudioChannelMask.aidl
+++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
@@ -57,8 +57,11 @@
  * checking the channel mask, the implementer should look for ways to fix it
  * with additional information outside of the mask.
  *
+ * TODO: this should be replaced with strings or other mechanisms that are easy to extend, in line
+ *     with the audio HAL conventions.
  * {@hide}
  */
+@VintfStability
 @Backing(type="int")
 enum AudioChannelMask {
     /**
@@ -113,42 +116,43 @@
      */
     OUT_HAPTIC_A = 0x20000000,
     OUT_HAPTIC_B = 0x10000000,
-// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
-//    OUT_MONO = OUT_FRONT_LEFT,
-//    OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
-//    OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
-//    OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-//    OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-//    OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-//    OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-//    OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-//    OUT_QUAD_BACK = OUT_QUAD,
-//    /**
-//     * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
-//     */
-//    OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-//    OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
-//    OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
-//    OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-//    OUT_5POINT1_BACK = OUT_5POINT1,
-//    /**
-//     * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
-//     */
-//    OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-//    OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-//    OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-//    OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
-//    /**
-//     * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
-//     */
-//    OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-//    OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-//    OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-//    OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
-//    OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
-//    OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
-//    OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
-//    OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
+    OUT_MONO = OUT_FRONT_LEFT,
+    OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
+    OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
+    OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+    OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+    OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+    OUT_QUAD_BACK = OUT_QUAD,
+    /**
+     * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
+     */
+    OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+    OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
+    OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
+    OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+    OUT_5POINT1_BACK = OUT_5POINT1,
+    /**
+     * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
+     */
+    OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+    OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+    OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
+    /**
+     * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
+     */
+    OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+    OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+    OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
+    OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
+    OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
+    OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+    OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
     /**
      * These are bits only, not complete values
      *
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 50dd796..d128561 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -27,10 +27,12 @@
  *
  * {@hide}
  */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
 parcelable AudioConfig {
     int sampleRateHz;
     int channelMask;
-    AudioFormat format;
+    AudioFormat format = AudioFormat.INVALID;
     AudioOffloadInfo offloadInfo;
     long frameCount;
 }
diff --git a/media/aidl/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
index aadc8e2..73fbca2 100644
--- a/media/aidl/android/media/audio/common/AudioFormat.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormat.aidl
@@ -32,6 +32,7 @@
  *
  * {@hide}
  */
+@VintfStability
 @Backing(type="int")
 enum AudioFormat {
    INVALID = 0xFFFFFFFF,
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index ec10d711..7be5e6a 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -28,17 +28,19 @@
  *
  * {@hide}
  */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
 parcelable AudioOffloadInfo {
     int sampleRateHz;
     int channelMask;
-    AudioFormat format;
-    AudioStreamType streamType;
+    AudioFormat format = AudioFormat.INVALID;
+    AudioStreamType streamType = AudioStreamType.INVALID;
     int bitRatePerSecond;
     long durationMicroseconds;
     boolean hasVideo;
     boolean isStreaming;
     int bitWidth;
     int bufferSize;
-    AudioUsage usage;
+    AudioUsage usage = AudioUsage.INVALID;
 }
 
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index c545667..8b70367 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -26,8 +26,14 @@
  *
  * {@hide}
  */
+@VintfStability
 @Backing(type="int")
 enum AudioStreamType {
+    /**
+     * Used as default value in parcelables to indicate that a value was not set.
+     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     */
+    INVALID = -2,
     DEFAULT = -1,
     MIN = 0,
     VOICE_CALL = 0,
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index ef34816..028eefe 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -22,8 +22,14 @@
 /**
  * {@hide}
  */
+@VintfStability
 @Backing(type="int")
 enum AudioUsage {
+    /**
+     * Used as default value in parcelables to indicate that a value was not set.
+     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     */
+    INVALID = -1,
     UNKNOWN = 0,
     MEDIA = 1,
     VOICE_COMMUNICATION = 2,
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 3638904..5249786 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -20,6 +20,7 @@
  *
  * {@hide}
  */
+@JavaDerive(equals = true, toString = true)
 parcelable Identity {
     /** Linux user ID. */
     int uid = -1;
diff --git a/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
new file mode 100644
index 0000000..7b0825b
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * AudioCapabilities supported by the implemented HAL driver.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioCapabilities {
+    /**
+     * If set the underlying module supports AEC.
+     */
+    ECHO_CANCELLATION = 1 << 0,
+    /**
+     * If set, the underlying module supports noise suppression.
+     */
+    NOISE_SUPPRESSION = 1 << 1,
+}
diff --git a/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
new file mode 100644
index 0000000..3fcba40
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * A recognition confidence level.
+ * This type is used to represent either a threshold or an actual detection confidence level.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable ConfidenceLevel {
+    /** user ID. */
+    int userId;
+    /**
+     * Confidence level in percent (0 - 100).
+     * <ul>
+     * <li>Min level for recognition configuration
+     * <li>Detected level for recognition event.
+     * </ul>
+     */
+    int levelPercent;
+}
diff --git a/media/aidl/android/media/soundtrigger/ModelParameter.aidl b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
new file mode 100644
index 0000000..9484008
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Model specific parameters to be used with parameter set and get APIs.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum ModelParameter {
+    /**
+     * Placeholder for invalid model parameter used for returning error or
+     * passing an invalid value.
+     */
+    INVALID = -1,
+
+    /**
+     * Controls the sensitivity threshold adjustment factor for a given model.
+     * Negative value corresponds to less sensitive model (high threshold) and
+     * a positive value corresponds to a more sensitive model (low threshold).
+     * Default value is 0.
+     */
+    THRESHOLD_FACTOR = 0,
+}
diff --git a/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
new file mode 100644
index 0000000..e7c616b
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Value range for a model parameter.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable ModelParameterRange {
+    /** Minimum (inclusive) */
+    int minInclusive;
+    /** Maximum (inclusive) */
+    int maxInclusive;
+}
diff --git a/media/aidl/android/media/soundtrigger/Phrase.aidl b/media/aidl/android/media/soundtrigger/Phrase.aidl
new file mode 100644
index 0000000..077db21
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/Phrase.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Key phrase descriptor.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable Phrase {
+    /** Unique keyphrase ID assigned at enrollment time. */
+    int id;
+    /** Recognition modes supported by this key phrase (bitfield of RecognitionMode enum). */
+    int recognitionModes;
+    /** List of users IDs associated with this key phrase. */
+    int[] users;
+    /** Locale - Java Locale style (e.g. en_US). */
+    String locale;
+    /** Phrase text. */
+    String text;
+}
diff --git a/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
new file mode 100644
index 0000000..654f7c2
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.RecognitionEvent;
+
+/**
+ * An event that gets sent to indicate a phrase recognition (or aborting of the recognition
+   process).
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable PhraseRecognitionEvent {
+    /** Common recognition event. */
+    RecognitionEvent common;
+    /** List of descriptors for each recognized key phrase */
+    PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
new file mode 100644
index 0000000..eb523eb
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.ConfidenceLevel;
+
+/**
+ * Specialized recognition event for key phrase detection.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable PhraseRecognitionExtra {
+    /** Keyphrase ID */
+    int id;
+    /** Bitfield, indexed by RecognitionMode. */
+    int recognitionModes;
+    /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER. Value is between 0-100. */
+    int confidenceLevel;
+    /** Number of user confidence levels */
+    ConfidenceLevel[] levels;
+}
diff --git a/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
new file mode 100644
index 0000000..e0ffdee
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Phrase;
+
+/**
+ * Specialized sound model for key phrase detection.
+ * Proprietary representation of key phrases in binary data must match
+ * information indicated by phrases field.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable PhraseSoundModel {
+    /** Common part of sound model descriptor */
+    SoundModel common;
+    /** List of descriptors for key phrases supported by this sound model */
+    Phrase[] phrases;
+}
diff --git a/media/aidl/android/media/soundtrigger/Properties.aidl b/media/aidl/android/media/soundtrigger/Properties.aidl
new file mode 100644
index 0000000..efa1b6a
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/Properties.aidl
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Capabilities of a sound trigger module.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable Properties {
+    /** Implementor name */
+    String   implementor;
+    /** Implementation description */
+    String   description;
+    /** Implementation version */
+    int version;
+    /**
+     * Unique implementation ID. The UUID must change with each version of
+       the engine implementation */
+    String     uuid;
+    /**
+     * String naming the architecture used for running the supported models.
+     * (eg. a platform running models on a DSP could implement this string to convey the DSP
+     * architecture used)
+     * This property is supported for soundtrigger HAL v2.3 and above.
+     * If running a previous version, the string will be empty.
+     */
+    String supportedModelArch;
+    /** Maximum number of concurrent sound models loaded */
+    int maxSoundModels;
+    /** Maximum number of key phrases */
+    int maxKeyPhrases;
+    /** Maximum number of concurrent users detected */
+    int maxUsers;
+    /** All supported modes. Bitfield, indexed by RecognitionMode. */
+    int recognitionModes;
+    /** Supports seamless transition from detection to capture */
+    boolean     captureTransition;
+    /** Maximum buffering capacity in ms if captureTransition is true */
+    int maxBufferMs;
+    /** Supports capture by other use cases while detection is active */
+    boolean     concurrentCapture;
+    /** Returns the trigger capture in event */
+    boolean     triggerInEvent;
+    /**
+     * Rated power consumption when detection is active with TDB
+     * silence/sound/speech ratio */
+    int powerConsumptionMw;
+    /**
+     * Bit field encoding of the AudioCapabilities
+     * supported by the firmware.
+     * This property is supported for soundtrigger HAL v2.3 and above.
+     * If running a previous version, this value will be 0.
+     */
+    int audioCapabilities;
+}
diff --git a/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
new file mode 100644
index 0000000..a00f0e5
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.PhraseRecognitionExtra;
+
+/**
+ * Configuration for tuning behavior of an active recognition process.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable RecognitionConfig {
+    /* Capture and buffer audio for this recognition instance. */
+    boolean captureRequested;
+
+    /* Configuration for each key phrase. */
+    PhraseRecognitionExtra[] phraseRecognitionExtras;
+
+    /**
+     * Bit field encoding of the AudioCapabilities
+     * supported by the firmware.
+     */
+    int audioCapabilities;
+
+    /** Capture configuration data. Content is implementation-defined. */
+    byte[] data;
+}
diff --git a/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
new file mode 100644
index 0000000..94668a3
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.audio.common.AudioConfig;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModelType;
+
+/**
+ * An event that gets sent to indicate a recognition (or aborting of the recognition process).
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable RecognitionEvent {
+    /** Recognition status. */
+    RecognitionStatus status = RecognitionStatus.INVALID;
+    /** Event type, same as sound model type. */
+    SoundModelType type = SoundModelType.INVALID;
+    /** Is it possible to capture audio from this utterance buffered by the implementation. */
+    boolean captureAvailable;
+    /**
+     * Delay in ms between end of model detection and start of audio available for capture.
+     * A negative value is possible (e.g. if key phrase is also available for Capture.
+     */
+    int captureDelayMs;
+    /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+    int capturePreambleMs;
+    /** If true, the 'data' field below contains the capture of the trigger sound. */
+    boolean triggerInData;
+    /**
+     * Audio format of either the trigger in event data or to use for capture of the rest of the
+     * utterance. May be null when no audio is available for this event type.
+     */
+    @nullable AudioConfig audioConfig;
+    /** Additional data. */
+    byte[] data;
+}
diff --git a/media/aidl/android/media/soundtrigger/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
new file mode 100644
index 0000000..ce2cffe
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Recognition mode.
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum RecognitionMode {
+    /** Simple voice trigger. */
+    VOICE_TRIGGER       = 0x1,
+    /** Trigger only if one user in model identified. */
+    USER_IDENTIFICATION = 0x2,
+    /** Trigger only if one user in model authenticated. */
+    USER_AUTHENTICATION = 0x4,
+    /** Generic sound trigger. */
+    GENERIC_TRIGGER     = 0x8,
+}
diff --git a/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
new file mode 100644
index 0000000..cccf0f3
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * A status for indicating the type of a recognition event.
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum RecognitionStatus {
+    /**
+     * Used as default value in parcelables to indicate that a value was not set.
+     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     */
+    INVALID = -1,
+    /** Recognition success. */
+    SUCCESS = 0,
+    /** Recognition aborted (e.g. capture preempted by another use-case. */
+    ABORTED = 1,
+    /** Recognition failure. */
+    FAILURE = 2,
+    /**
+    * Recognition event was triggered by a forceRecognitionEvent request, not by the DSP.
+    * Note that forced detections *do not* stop the active recognition, unlike the other types.
+    */
+    FORCED = 3
+}
diff --git a/media/aidl/android/media/soundtrigger/SoundModel.aidl b/media/aidl/android/media/soundtrigger/SoundModel.aidl
new file mode 100644
index 0000000..94244d0
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/SoundModel.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+import android.media.soundtrigger.SoundModelType;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Base sound model descriptor. This struct can be extended for various specific types by way of
+ * aggregation.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable SoundModel {
+    /** Model type. */
+    SoundModelType type = SoundModelType.INVALID;
+    /** Unique sound model ID. */
+    String uuid;
+    /**
+     * Unique vendor ID. Identifies the engine the sound model
+     * was build for */
+    String vendorUuid;
+    /** Opaque data transparent to Android framework. May be null if dataSize is 0. */
+    @nullable ParcelFileDescriptor data;
+    /** Size of the above data, in bytes. */
+    int dataSize;
+}
diff --git a/media/aidl/android/media/soundtrigger/SoundModelType.aidl b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
new file mode 100644
index 0000000..34a9376
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * Sound model type.
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum SoundModelType {
+    /**
+     * Used as default value in parcelables to indicate that a value was not set.
+     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     */
+    INVALID = -1,
+    /** Key phrase sound models */
+    KEYPHRASE = 0,
+    /** All models other than keyphrase */
+    GENERIC = 1,
+}
diff --git a/media/aidl/android/media/soundtrigger/Status.aidl b/media/aidl/android/media/soundtrigger/Status.aidl
new file mode 100644
index 0000000..ca1487f
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger/Status.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger;
+
+/**
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum Status {
+    /**
+     * Used as default value in parcelables to indicate that a value was not set.
+     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     */
+    INVALID = -1,
+    /** Success. */
+    SUCCESS = 0,
+    /** Failure due to resource contention. This is typically a temporary condition. */
+    RESOURCE_CONTENTION = 1,
+    /** Operation is not supported in this implementation. This is a permanent condition. */
+    OPERATION_NOT_SUPPORTED = 2,
+    /** Temporary lack of permission. */
+    TEMPORARY_PERMISSION_DENIED = 3,
+    /** The object on which this method is called is dead and all of its state is lost. */
+    DEAD_OBJECT = 4,
+    /**
+     * Unexpected internal error has occurred. Usually this will result in the service rebooting
+     * shortly after. The client should treat the state of the server as undefined.
+     */
+    INTERNAL_ERROR = 5,
+}
diff --git a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
deleted file mode 100644
index 97a8849..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
+++ /dev/null
@@ -1,32 +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.media.soundtrigger_middleware;
-
-/**
- * AudioCapabilities supported by the implemented HAL driver.
- * @hide
- */
-@Backing(type="int")
-enum AudioCapabilities {
-    /**
-     * If set the underlying module supports AEC.
-     */
-    ECHO_CANCELLATION = 1 << 0,
-    /**
-     * If set, the underlying module supports noise suppression.
-     */
-    NOISE_SUPPRESSION = 1 << 1,
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
deleted file mode 100644
index 3dbc705..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
+++ /dev/null
@@ -1,35 +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.media.soundtrigger_middleware;
-
-/**
- * A recognition confidence level.
- * This type is used to represent either a threshold or an actual detection confidence level.
- *
- * {@hide}
- */
-parcelable ConfidenceLevel {
-    /** user ID. */
-    int userId;
-    /**
-     * Confidence level in percent (0 - 100).
-     * <ul>
-     * <li>Min level for recognition configuration
-     * <li>Detected level for recognition event.
-     * </ul>
-     */
-    int levelPercent;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 726af76..6092ac5 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -15,8 +15,8 @@
  */
 package android.media.soundtrigger_middleware;
 
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionEvent;
 
 /**
  * Main interface for a client to get notifications of events coming from this module.
@@ -28,22 +28,29 @@
      * Invoked whenever a recognition event is triggered (typically, on recognition, but also in
      * case of external aborting of a recognition or a forced recognition event - see the status
      * code in the event for determining).
+     * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+     * callback.
      */
-    void onRecognition(int modelHandle, in RecognitionEvent event);
+    void onRecognition(int modelHandle, in RecognitionEvent event, int captureSession);
      /**
       * Invoked whenever a phrase recognition event is triggered (typically, on recognition, but
       * also in case of external aborting of a recognition or a forced recognition event - see the
       * status code in the event for determining).
+      * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+      * callback.
       */
-    void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event);
+    void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event, int captureSession);
     /**
-     * Notifies the client the recognition has become available after previously having been
-     * unavailable, or vice versa. This method will always be invoked once immediately after
-     * attachment, and then every time there is a change in availability.
-     * When availability changes from available to unavailable, all active recognitions are aborted,
-     * and this event will be sent in addition to the abort event.
+     * Notifies the client that some start/load operations that have previously failed for resource
+     * reasons (threw a ServiceSpecificException(RESOURCE_CONTENTION) or have been preempted) may
+     * now succeed. This is not a guarantee, but a hint for the client to retry.
      */
-    void onRecognitionAvailabilityChange(boolean available);
+    void onResourcesAvailable();
+    /**
+     * Notifies the client that a model had been preemptively unloaded by the service.
+     * The caller may retry after the next onRecognitionAvailabilityChange() callback.
+     */
+    void onModelUnloaded(int modelHandle);
     /**
      * Notifies the client that the associated module has crashed and restarted. The module instance
      * is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
index c4a5785..0b46fd4 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
@@ -15,11 +15,11 @@
  */
 package android.media.soundtrigger_middleware;
 
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
 
 /**
  * A sound-trigger module.
@@ -75,6 +75,9 @@
      * Once a recognition event is passed to the client, the recognition automatically become
      * inactive, unless the event is of the RecognitionStatus.FORCED kind. Client can also shut down
      * the recognition explicitly, via stopRecognition.
+     *
+     * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+     * resources required for starting the model are currently consumed by other clients.
      */
     void startRecognition(int modelHandle, in RecognitionConfig config);
 
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
deleted file mode 100644
index 0993627..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
+++ /dev/null
@@ -1,38 +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.media.soundtrigger_middleware;
-
-/**
- * Model specific parameters to be used with parameter set and get APIs.
- *
- * {@hide}
- */
-@Backing(type="int")
-enum ModelParameter {
-    /**
-     * Placeholder for invalid model parameter used for returning error or
-     * passing an invalid value.
-     */
-    INVALID = -1,
-
-    /**
-     * Controls the sensitivity threshold adjustment factor for a given model.
-     * Negative value corresponds to less sensitive model (high threshold) and
-     * a positive value corresponds to a more sensitive model (low threshold).
-     * Default value is 0.
-     */
-    THRESHOLD_FACTOR = 0,
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
deleted file mode 100644
index d6948a8..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ /dev/null
@@ -1,28 +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.media.soundtrigger_middleware;
-
-/**
- * Value range for a model parameter.
- *
- * {@hide}
- */
-parcelable ModelParameterRange {
-    /** Minimum (inclusive) */
-    int minInclusive;
-    /** Maximum (inclusive) */
-    int maxInclusive;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
deleted file mode 100644
index 98a489f8..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
+++ /dev/null
@@ -1,34 +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.media.soundtrigger_middleware;
-
-/**
- * Key phrase descriptor.
- *
- * {@hide}
- */
-parcelable Phrase {
-    /** Unique keyphrase ID assigned at enrollment time. */
-    int id;
-    /** Recognition modes supported by this key phrase (bitfield of RecognitionMode enum). */
-    int recognitionModes;
-    /** List of users IDs associated with this key phrase. */
-    int[] users;
-    /** Locale - Java Locale style (e.g. en_US). */
-    String locale;
-    /** Phrase text. */
-    String text;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
deleted file mode 100644
index 6a3ec61..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
+++ /dev/null
@@ -1,31 +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.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-
-/**
- * An event that gets sent to indicate a phrase recognition (or aborting of the recognition
-   process).
- * {@hide}
- */
-parcelable PhraseRecognitionEvent {
-    /** Common recognition event. */
-    RecognitionEvent common;
-    /** List of descriptors for each recognized key phrase */
-    PhraseRecognitionExtra[] phraseExtras;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
deleted file mode 100644
index cb96bf3..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
+++ /dev/null
@@ -1,35 +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.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-
-/**
- * Specialized recognition event for key phrase detection.
- * {@hide}
- */
-parcelable PhraseRecognitionExtra {
-    // TODO(ytai): Constants / enums.
-
-    /** keyphrase ID */
-    int id;
-    /** recognition modes used for this keyphrase */
-    int recognitionModes;
-    /** confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
-    int confidenceLevel;
-    /** number of user confidence levels */
-    ConfidenceLevel[] levels;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
deleted file mode 100644
index 81028c1..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
+++ /dev/null
@@ -1,32 +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.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.Phrase;
-
-/**
- * Specialized sound model for key phrase detection.
- * Proprietary representation of key phrases in binary data must match
- * information indicated by phrases field.
- * {@hide}
- */
-parcelable PhraseSoundModel {
-    /** Common part of sound model descriptor */
-    SoundModel common;
-    /** List of descriptors for key phrases supported by this sound model */
-    Phrase[] phrases;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
deleted file mode 100644
index 5c0eeb1..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ /dev/null
@@ -1,39 +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.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-
-/**
- * Configuration for tuning behavior of an active recognition process.
- * {@hide}
- */
-parcelable RecognitionConfig {
-    /* Capture and buffer audio for this recognition instance. */
-    boolean captureRequested;
-
-    /* Configuration for each key phrase. */
-    PhraseRecognitionExtra[] phraseRecognitionExtras;
-
-    /**
-     * Bit field encoding of the AudioCapabilities
-     * supported by the firmware.
-     */
-    int audioCapabilities;
-
-    /** Opaque capture configuration data. */
-    byte[] data;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
deleted file mode 100644
index a237ec1..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ /dev/null
@@ -1,51 +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.media.soundtrigger_middleware;
-
-import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModelType;
-
-/**
- * An event that gets sent to indicate a recognition (or aborting of the recognition process).
- * {@hide}
- */
-parcelable RecognitionEvent {
-    /** Recognition status. */
-    RecognitionStatus status;
-    /** Event type, same as sound model type. */
-    SoundModelType type;
-    /** Is it possible to capture audio from this utterance buffered by the implementation. */
-    boolean captureAvailable;
-    /* Audio session ID. framework use. */
-    int captureSession;
-    /**
-     * Delay in ms between end of model detection and start of audio available for capture.
-     * A negative value is possible (e.g. if key phrase is also available for Capture.
-     */
-    int captureDelayMs;
-    /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
-    int capturePreambleMs;
-    /** If true, the 'data' field below contains the capture of the trigger sound. */
-    boolean triggerInData;
-    /**
-     * Audio format of either the trigger in event data or to use for capture of the rest of the
-     * utterance. May be null when no audio is available for this event type.
-     */
-    @nullable AudioConfig audioConfig;
-    /** Additional data. */
-    byte[] data;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
deleted file mode 100644
index d8bfff4..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
+++ /dev/null
@@ -1,32 +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.media.soundtrigger_middleware;
-
-/**
- * Recognition mode.
- * {@hide}
- */
-@Backing(type="int")
-enum RecognitionMode {
-    /** Simple voice trigger. */
-    VOICE_TRIGGER       = 0x1,
-    /** Trigger only if one user in model identified. */
-    USER_IDENTIFICATION = 0x2,
-    /** Trigger only if one user in model authenticated. */
-    USER_AUTHENTICATION = 0x4,
-    /** Generic sound trigger. */
-    GENERIC_TRIGGER     = 0x8,
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
deleted file mode 100644
index d563edc..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
+++ /dev/null
@@ -1,35 +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.media.soundtrigger_middleware;
-
-/**
- * A status for indicating the type of a recognition event.
- * {@hide}
- */
-@Backing(type="int")
-enum RecognitionStatus {
-    /** Recognition success. */
-    SUCCESS = 0,
-    /** Recognition aborted (e.g. capture preempted by another use-case. */
-    ABORTED = 1,
-    /** Recognition failure. */
-    FAILURE = 2,
-    /**
-    * Recognition event was triggered by a forceRecognitionEvent request, not by the DSP.
-    * Note that forced detections *do not* stop the active recognition, unlike the other types.
-    */
-    FORCED = 3
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
deleted file mode 100644
index 8186fb7..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
+++ /dev/null
@@ -1,39 +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.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.os.ParcelFileDescriptor;
-
-/**
- * Base sound model descriptor. This struct can be extended for various specific types by way of
- * aggregation.
- * {@hide}
- */
-parcelable SoundModel {
-    /** Model type. */
-    SoundModelType type;
-    /** Unique sound model ID. */
-    String uuid;
-    /**
-     * Unique vendor ID. Identifies the engine the sound model
-     * was build for */
-    String vendorUuid;
-    /** Opaque data transparent to Android framework. May be null if dataSize is 0. */
-    @nullable ParcelFileDescriptor data;
-    /** Size of the above data, in bytes. */
-    int dataSize;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
deleted file mode 100644
index f2abc9a..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
+++ /dev/null
@@ -1,30 +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.media.soundtrigger_middleware;
-
-/**
- * Sound model type.
- * {@hide}
- */
-@Backing(type="int")
-enum SoundModelType {
-    /** Unspecified sound model type */
-    UNKNOWN = -1,
-    /** Key phrase sound models */
-    KEYPHRASE = 0,
-    /** All models other than keyphrase */
-    GENERIC = 1,
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
index 667135f..6c210bf 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
@@ -15,17 +15,18 @@
  */
 package android.media.soundtrigger_middleware;
 
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.media.soundtrigger.Properties;
 
 /**
  * A descriptor of an available sound trigger module, containing the handle used to reference the
  * module, as well its capabilities.
  * {@hide}
  */
+@JavaDerive(equals = true, toString = true)
 parcelable SoundTriggerModuleDescriptor {
     /** Module handle to be used for attaching to it. */
     int handle;
     /** Module capabilities. */
-    SoundTriggerModuleProperties properties;
+    Properties properties;
 }
 
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
deleted file mode 100644
index 9c56e7b..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ /dev/null
@@ -1,68 +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.media.soundtrigger_middleware;
-
-/**
- * Capabilities of a sound trigger module.
- * {@hide}
- */
-parcelable SoundTriggerModuleProperties {
-    /** Implementor name */
-    String   implementor;
-    /** Implementation description */
-    String   description;
-    /** Implementation version */
-    int version;
-    /**
-     * Unique implementation ID. The UUID must change with each version of
-       the engine implementation */
-    String     uuid;
-    /**
-     * String naming the architecture used for running the supported models.
-     * (eg. a platform running models on a DSP could implement this string to convey the DSP
-     * architecture used)
-     * This property is supported for soundtrigger HAL v2.3 and above.
-     * If running a previous version, the string will be empty.
-     */
-    String supportedModelArch;
-    /** Maximum number of concurrent sound models loaded */
-    int maxSoundModels;
-    /** Maximum number of key phrases */
-    int maxKeyPhrases;
-    /** Maximum number of concurrent users detected */
-    int maxUsers;
-    /** All supported modes. e.g RecognitionMode.VOICE_TRIGGER */
-    int recognitionModes;
-    /** Supports seamless transition from detection to capture */
-    boolean     captureTransition;
-    /** Maximum buffering capacity in ms if captureTransition is true */
-    int maxBufferMs;
-    /** Supports capture by other use cases while detection is active */
-    boolean     concurrentCapture;
-    /** Returns the trigger capture in event */
-    boolean     triggerInEvent;
-    /**
-     * Rated power consumption when detection is active with TDB
-     * silence/sound/speech ratio */
-    int powerConsumptionMw;
-    /**
-     * Bit field encoding of the AudioCapabilities
-     * supported by the firmware.
-     * This property is supported for soundtrigger HAL v2.3 and above.
-     * If running a previous version, this value will be 0.
-     */
-    int audioCapabilities;
-}
diff --git a/media/aidl/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger_middleware/Status.aidl
deleted file mode 100644
index c7623f5..0000000
--- a/media/aidl/android/media/soundtrigger_middleware/Status.aidl
+++ /dev/null
@@ -1,38 +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.media.soundtrigger_middleware;
-
-/**
- * {@hide}
- */
-@Backing(type="int")
-enum Status {
-    /** Success. */
-    SUCCESS = 0,
-    /** Failure due to resource contention. This is typically a temporary condition. */
-    RESOURCE_CONTENTION = 1,
-    /** Operation is not supported in this implementation. This is a permanent condition. */
-    OPERATION_NOT_SUPPORTED = 2,
-    /** Temporary lack of permission. */
-    TEMPORARY_PERMISSION_DENIED = 3,
-    /** The object on which this method is called is dead and all of its state is lost. */
-    DEAD_OBJECT = 4,
-    /**
-     * Unexpected internal error has occurred. Usually this will result in the service rebooting
-     * shortly after. The client should treat the state of the server as undefined.
-     */
-    INTERNAL_ERROR = 5,
-}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
new file mode 100644
index 0000000..c3af3bf
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+  REPRESENTATION_POSITION = 0,
+  REPRESENTATION_INDEX = 2,
+  NONE = 0,
+  INVALID = -1073741824,
+  OUT_FRONT_LEFT = 1,
+  OUT_FRONT_RIGHT = 2,
+  OUT_FRONT_CENTER = 4,
+  OUT_LOW_FREQUENCY = 8,
+  OUT_BACK_LEFT = 16,
+  OUT_BACK_RIGHT = 32,
+  OUT_FRONT_LEFT_OF_CENTER = 64,
+  OUT_FRONT_RIGHT_OF_CENTER = 128,
+  OUT_BACK_CENTER = 256,
+  OUT_SIDE_LEFT = 512,
+  OUT_SIDE_RIGHT = 1024,
+  OUT_TOP_CENTER = 2048,
+  OUT_TOP_FRONT_LEFT = 4096,
+  OUT_TOP_FRONT_CENTER = 8192,
+  OUT_TOP_FRONT_RIGHT = 16384,
+  OUT_TOP_BACK_LEFT = 32768,
+  OUT_TOP_BACK_CENTER = 65536,
+  OUT_TOP_BACK_RIGHT = 131072,
+  OUT_TOP_SIDE_LEFT = 262144,
+  OUT_TOP_SIDE_RIGHT = 524288,
+  OUT_HAPTIC_A = 536870912,
+  OUT_HAPTIC_B = 268435456,
+  OUT_MONO = 1,
+  OUT_STEREO = 3,
+  OUT_2POINT1 = 11,
+  OUT_2POINT0POINT2 = 786435,
+  OUT_2POINT1POINT2 = 786443,
+  OUT_3POINT0POINT2 = 786439,
+  OUT_3POINT1POINT2 = 786447,
+  OUT_QUAD = 51,
+  OUT_QUAD_BACK = 51,
+  OUT_QUAD_SIDE = 1539,
+  OUT_SURROUND = 263,
+  OUT_PENTA = 55,
+  OUT_5POINT1 = 63,
+  OUT_5POINT1_BACK = 63,
+  OUT_5POINT1_SIDE = 1551,
+  OUT_5POINT1POINT2 = 786495,
+  OUT_5POINT1POINT4 = 184383,
+  OUT_6POINT1 = 319,
+  OUT_7POINT1 = 1599,
+  OUT_7POINT1POINT2 = 788031,
+  OUT_7POINT1POINT4 = 185919,
+  OUT_MONO_HAPTIC_A = 536870913,
+  OUT_STEREO_HAPTIC_A = 536870915,
+  OUT_HAPTIC_AB = 805306368,
+  OUT_MONO_HAPTIC_AB = 805306369,
+  OUT_STEREO_HAPTIC_AB = 805306371,
+  IN_LEFT = 4,
+  IN_RIGHT = 8,
+  IN_FRONT = 16,
+  IN_BACK = 32,
+  IN_LEFT_PROCESSED = 64,
+  IN_RIGHT_PROCESSED = 128,
+  IN_FRONT_PROCESSED = 256,
+  IN_BACK_PROCESSED = 512,
+  IN_PRESSURE = 1024,
+  IN_X_AXIS = 2048,
+  IN_Y_AXIS = 4096,
+  IN_Z_AXIS = 8192,
+  IN_BACK_LEFT = 65536,
+  IN_BACK_RIGHT = 131072,
+  IN_CENTER = 262144,
+  IN_LOW_FREQUENCY = 1048576,
+  IN_TOP_LEFT = 2097152,
+  IN_TOP_RIGHT = 4194304,
+  IN_VOICE_UPLINK = 16384,
+  IN_VOICE_DNLINK = 32768,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
new file mode 100644
index 0000000..c11eb76
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfig {
+  int sampleRateHz;
+  int channelMask;
+  android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+  android.media.audio.common.AudioOffloadInfo offloadInfo;
+  long frameCount;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
new file mode 100644
index 0000000..b7c8659
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+  INVALID = -1,
+  DEFAULT = 0,
+  PCM = 0,
+  MP3 = 16777216,
+  AMR_NB = 33554432,
+  AMR_WB = 50331648,
+  AAC = 67108864,
+  HE_AAC_V1 = 83886080,
+  HE_AAC_V2 = 100663296,
+  VORBIS = 117440512,
+  OPUS = 134217728,
+  AC3 = 150994944,
+  E_AC3 = 167772160,
+  DTS = 184549376,
+  DTS_HD = 201326592,
+  IEC61937 = 218103808,
+  DOLBY_TRUEHD = 234881024,
+  EVRC = 268435456,
+  EVRCB = 285212672,
+  EVRCWB = 301989888,
+  EVRCNW = 318767104,
+  AAC_ADIF = 335544320,
+  WMA = 352321536,
+  WMA_PRO = 369098752,
+  AMR_WB_PLUS = 385875968,
+  MP2 = 402653184,
+  QCELP = 419430400,
+  DSD = 436207616,
+  FLAC = 452984832,
+  ALAC = 469762048,
+  APE = 486539264,
+  AAC_ADTS = 503316480,
+  SBC = 520093696,
+  APTX = 536870912,
+  APTX_HD = 553648128,
+  AC4 = 570425344,
+  LDAC = 587202560,
+  MAT = 603979776,
+  AAC_LATM = 620756992,
+  CELT = 637534208,
+  APTX_ADAPTIVE = 654311424,
+  LHDC = 671088640,
+  LHDC_LL = 687865856,
+  APTX_TWSP = 704643072,
+  MAIN_MASK = -16777216,
+  SUB_MASK = 16777215,
+  PCM_SUB_16_BIT = 1,
+  PCM_SUB_8_BIT = 2,
+  PCM_SUB_32_BIT = 3,
+  PCM_SUB_8_24_BIT = 4,
+  PCM_SUB_FLOAT = 5,
+  PCM_SUB_24_BIT_PACKED = 6,
+  MP3_SUB_NONE = 0,
+  AMR_SUB_NONE = 0,
+  AAC_SUB_MAIN = 1,
+  AAC_SUB_LC = 2,
+  AAC_SUB_SSR = 4,
+  AAC_SUB_LTP = 8,
+  AAC_SUB_HE_V1 = 16,
+  AAC_SUB_SCALABLE = 32,
+  AAC_SUB_ERLC = 64,
+  AAC_SUB_LD = 128,
+  AAC_SUB_HE_V2 = 256,
+  AAC_SUB_ELD = 512,
+  AAC_SUB_XHE = 768,
+  VORBIS_SUB_NONE = 0,
+  E_AC3_SUB_JOC = 1,
+  MAT_SUB_1_0 = 1,
+  MAT_SUB_2_0 = 2,
+  MAT_SUB_2_1 = 3,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
new file mode 100644
index 0000000..b5d889e
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioOffloadInfo {
+  int sampleRateHz;
+  int channelMask;
+  android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+  android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+  int bitRatePerSecond;
+  long durationMicroseconds;
+  boolean hasVideo;
+  boolean isStreaming;
+  int bitWidth;
+  int bufferSize;
+  android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
new file mode 100644
index 0000000..915c668
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioStreamType {
+  INVALID = -2,
+  DEFAULT = -1,
+  MIN = 0,
+  VOICE_CALL = 0,
+  SYSTEM = 1,
+  RING = 2,
+  MUSIC = 3,
+  ALARM = 4,
+  NOTIFICATION = 5,
+  BLUETOOTH_SCO = 6,
+  ENFORCED_AUDIBLE = 7,
+  DTMF = 8,
+  TTS = 9,
+  ACCESSIBILITY = 10,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
new file mode 100644
index 0000000..f5130a4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioUsage {
+  INVALID = -1,
+  UNKNOWN = 0,
+  MEDIA = 1,
+  VOICE_COMMUNICATION = 2,
+  VOICE_COMMUNICATION_SIGNALLING = 3,
+  ALARM = 4,
+  NOTIFICATION = 5,
+  NOTIFICATION_TELEPHONY_RINGTONE = 6,
+  ASSISTANCE_ACCESSIBILITY = 11,
+  ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+  ASSISTANCE_SONIFICATION = 13,
+  GAME = 14,
+  VIRTUAL_SOURCE = 15,
+  ASSISTANT = 16,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
new file mode 100644
index 0000000..5d88305
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioCapabilities {
+  ECHO_CANCELLATION = 1,
+  NOISE_SUPPRESSION = 2,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
new file mode 100644
index 0000000..5127a11
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ConfidenceLevel {
+  int userId;
+  int levelPercent;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
new file mode 100644
index 0000000..aadbf80
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum ModelParameter {
+  INVALID = -1,
+  THRESHOLD_FACTOR = 0,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
new file mode 100644
index 0000000..f29b728
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ModelParameterRange {
+  int minInclusive;
+  int maxInclusive;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
new file mode 100644
index 0000000..11029ba
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Phrase {
+  int id;
+  int recognitionModes;
+  int[] users;
+  String locale;
+  String text;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
new file mode 100644
index 0000000..b75d1b8
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionEvent {
+  android.media.soundtrigger.RecognitionEvent common;
+  android.media.soundtrigger.PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
new file mode 100644
index 0000000..e417c69
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionExtra {
+  int id;
+  int recognitionModes;
+  int confidenceLevel;
+  android.media.soundtrigger.ConfidenceLevel[] levels;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
new file mode 100644
index 0000000..b4b3854
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseSoundModel {
+  android.media.soundtrigger.SoundModel common;
+  android.media.soundtrigger.Phrase[] phrases;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
new file mode 100644
index 0000000..068db52
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Properties {
+  String implementor;
+  String description;
+  int version;
+  String uuid;
+  String supportedModelArch;
+  int maxSoundModels;
+  int maxKeyPhrases;
+  int maxUsers;
+  int recognitionModes;
+  boolean captureTransition;
+  int maxBufferMs;
+  boolean concurrentCapture;
+  boolean triggerInEvent;
+  int powerConsumptionMw;
+  int audioCapabilities;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
new file mode 100644
index 0000000..63cd2cbb
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionConfig {
+  boolean captureRequested;
+  android.media.soundtrigger.PhraseRecognitionExtra[] phraseRecognitionExtras;
+  int audioCapabilities;
+  byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
new file mode 100644
index 0000000..e6cfb6b
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionEvent {
+  android.media.soundtrigger.RecognitionStatus status = android.media.soundtrigger.RecognitionStatus.INVALID;
+  android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+  boolean captureAvailable;
+  int captureDelayMs;
+  int capturePreambleMs;
+  boolean triggerInData;
+  @nullable android.media.audio.common.AudioConfig audioConfig;
+  byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
new file mode 100644
index 0000000..5882910
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionMode {
+  VOICE_TRIGGER = 1,
+  USER_IDENTIFICATION = 2,
+  USER_AUTHENTICATION = 4,
+  GENERIC_TRIGGER = 8,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
new file mode 100644
index 0000000..7881c28
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionStatus {
+  INVALID = -1,
+  SUCCESS = 0,
+  ABORTED = 1,
+  FAILURE = 2,
+  FORCED = 3,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
new file mode 100644
index 0000000..fe38264
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SoundModel {
+  android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+  String uuid;
+  String vendorUuid;
+  @nullable ParcelFileDescriptor data;
+  int dataSize;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
new file mode 100644
index 0000000..ac78641
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum SoundModelType {
+  INVALID = -1,
+  KEYPHRASE = 0,
+  GENERIC = 1,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
new file mode 100644
index 0000000..29f3167
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Status {
+  INVALID = -1,
+  SUCCESS = 0,
+  RESOURCE_CONTENTION = 1,
+  OPERATION_NOT_SUPPORTED = 2,
+  TEMPORARY_PERMISSION_DENIED = 3,
+  DEAD_OBJECT = 4,
+  INTERNAL_ERROR = 5,
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a031b4c..07444ea 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -334,7 +334,19 @@
     };
 
     /**
+     * @hide
+     */
+    @TestApi
+    public static int[] getSdkUsages() {
+        return SDK_USAGES;
+    }
+
+    /**
      * Flag defining a behavior where the audibility of the sound will be ensured by the system.
+     * To ensure sound audibility, the system only uses built-in speakers or wired headphones
+     * and specifically excludes wireless audio devices.
+     * <p>Note this flag should only be used for sounds subject to regulatory behaviors in some
+     * countries, such as for camera shutter sound, and not for routing behaviors.
      */
     public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
     /**
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 1644ec8..a8199c4 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -175,6 +175,28 @@
  * <br>These masks are an ORed composite of individual channel masks. For example
  * {@link #CHANNEL_OUT_STEREO} is composed of {@link #CHANNEL_OUT_FRONT_LEFT} and
  * {@link #CHANNEL_OUT_FRONT_RIGHT}.
+ * <p>
+ * The following diagram represents the layout of the output channels, as seen from above
+ * the listener (in the center at the "lis" position, facing the front-center channel).
+ * <pre>
+ *       TFL ----- TFC ----- TFR     T is Top
+ *       |  \       |       /  |
+ *       |   FL --- FC --- FR  |     F is Front
+ *       |   |\     |     /|   |
+ *       |   | BFL-BFC-BFR |   |     BF is Bottom Front
+ *       |   |             |   |
+ *       |   FWL   lis   FWR   |     W is Wide
+ *       |   |             |   |
+ *      TSL  SL    TC     SR  TSR    S is Side
+ *       |   |             |   |
+ *       |   BL --- BC -- BR   |     B is Back
+ *       |  /               \  |
+ *       TBL ----- TBC ----- TBR     C is Center, L/R is Left/Right
+ * </pre>
+ * All "T" (top) channels are above the listener, all "BF" (bottom-front) channels are below the
+ * listener, all others are in the listener's horizontal plane. When used in conjunction, LFE1 and
+ * LFE2 are below the listener, when used alone, LFE plane is undefined.
+ * See the channel definitions for the abbreviations
  *
  * <h5 id="channelIndexMask">Channel index masks</h5>
  * Channel index masks are introduced in API {@link android.os.Build.VERSION_CODES#M}. They allow
@@ -417,43 +439,62 @@
 
     // Output channel mask definitions below are translated to the native values defined in
     //  in /system/media/audio/include/system/audio.h in the JNI code of AudioTrack
+    /** Front left output channel (see FL in channel diagram) */
     public static final int CHANNEL_OUT_FRONT_LEFT = 0x4;
+    /** Front right output channel (see FR in channel diagram) */
     public static final int CHANNEL_OUT_FRONT_RIGHT = 0x8;
+    /** Front center output channel (see FC in channel diagram) */
     public static final int CHANNEL_OUT_FRONT_CENTER = 0x10;
+    /** LFE "low frequency effect" channel
+     * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY_2}, it is intended
+     * to contain the left low-frequency effect signal, also referred to as "LFE1"
+     * in ITU-R BS.2159-8 */
     public static final int CHANNEL_OUT_LOW_FREQUENCY = 0x20;
+    /** Back left output channel (see BL in channel diagram) */
     public static final int CHANNEL_OUT_BACK_LEFT = 0x40;
+    /** Back right output channel (see BR in channel diagram) */
     public static final int CHANNEL_OUT_BACK_RIGHT = 0x80;
     public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
     public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
+    /** Back center output channel (see BC in channel diagram) */
     public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
+    /** Side left output channel (see SL in channel diagram) */
     public static final int CHANNEL_OUT_SIDE_LEFT =         0x800;
+    /** Side right output channel (see SR in channel diagram) */
     public static final int CHANNEL_OUT_SIDE_RIGHT =       0x1000;
-    /** @hide */
+    /** Top center (above listener) output channel (see TC in channel diagram) */
     public static final int CHANNEL_OUT_TOP_CENTER =       0x2000;
-    /** @hide */
+    /** Top front left output channel (see TFL in channel diagram above FL) */
     public static final int CHANNEL_OUT_TOP_FRONT_LEFT =   0x4000;
-    /** @hide */
+    /** Top front center output channel (see TFC in channel diagram above FC) */
     public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 0x8000;
-    /** @hide */
+    /** Top front right output channel (see TFR in channel diagram above FR) */
     public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 0x10000;
-    /** @hide */
+    /** Top back left output channel (see TBL in channel diagram above BL) */
     public static final int CHANNEL_OUT_TOP_BACK_LEFT =   0x20000;
-    /** @hide */
+    /** Top back center output channel (see TBC in channel diagram above BC) */
     public static final int CHANNEL_OUT_TOP_BACK_CENTER = 0x40000;
-    /** @hide */
+    /** Top back right output channel (see TBR in channel diagram above BR) */
     public static final int CHANNEL_OUT_TOP_BACK_RIGHT =  0x80000;
-    /** @hide */
+    /** Top side left output channel (see TSL in channel diagram above SL) */
     public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 0x100000;
-    /** @hide */
+    /** Top side right output channel (see TSR in channel diagram above SR) */
     public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 0x200000;
-    /** @hide */
+    /** Bottom front left output channel (see BFL in channel diagram below FL) */
     public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 0x400000;
-    /** @hide */
+    /** Bottom front center output channel (see BFC in channel diagram below FC) */
     public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 0x800000;
-    /** @hide */
+    /** Bottom front right output channel (see BFR in channel diagram below FR) */
     public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 0x1000000;
-    /** @hide */
+    /** The second LFE channel
+     * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY}, it is intended
+     * to contain the right low-frequency effect signal, also referred to as "LFE2"
+     * in ITU-R BS.2159-8 */
     public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 0x2000000;
+    /** Front wide left output channel (see FWL in channel diagram) */
+    public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 0x4000000;
+    /** Front wide right output channel (see FWR in channel diagram) */
+    public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 0x8000000;
 
     public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
     public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
@@ -466,6 +507,7 @@
     public static final int CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER);
     // aka 5POINT1_BACK
+    /** Output channel mask for 5.1 */
     public static final int CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
     /** @hide */
@@ -477,26 +519,39 @@
     @Deprecated    public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
             CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
+    /** Output channel mask for 7.1 */
     // matches AUDIO_CHANNEL_OUT_7POINT1
     public static final int CHANNEL_OUT_7POINT1_SURROUND = (
             CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT |
             CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
             CHANNEL_OUT_LOW_FREQUENCY);
-    /** @hide */
+    /** Output channel mask for 5.1.2
+     *  Same as 5.1 with the addition of left and right top channels */
     public static final int CHANNEL_OUT_5POINT1POINT2 = (CHANNEL_OUT_5POINT1 |
             CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
-    /** @hide */
+    /** Output channel mask for 5.1.4
+     * Same as 5.1 with the addition of four top channels */
     public static final int CHANNEL_OUT_5POINT1POINT4 = (CHANNEL_OUT_5POINT1 |
             CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
             CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
-    /** @hide */
+    /** Output channel mask for 7.1.2
+     * Same as 7.1 with the addition of left and right top channels*/
     public static final int CHANNEL_OUT_7POINT1POINT2 = (CHANNEL_OUT_7POINT1_SURROUND |
             CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
-    /** @hide */
+    /** Output channel mask for 7.1.4
+     *  Same as 7.1 with the addition of four top channels */
     public static final int CHANNEL_OUT_7POINT1POINT4 = (CHANNEL_OUT_7POINT1_SURROUND |
             CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
             CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
+    /** Output channel mask for 9.1.4
+     * Same as 7.1.4 with the addition of left and right front wide channels */
+    public static final int CHANNEL_OUT_9POINT1POINT4 = (CHANNEL_OUT_7POINT1POINT4
+            | CHANNEL_OUT_FRONT_WIDE_LEFT | CHANNEL_OUT_FRONT_WIDE_RIGHT);
+    /** Output channel mask for 9.1.6
+     * Same as 9.1.4 with the addition of left and right top side channels */
+    public static final int CHANNEL_OUT_9POINT1POINT6 = (CHANNEL_OUT_9POINT1POINT4
+            | CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
     /** @hide */
     public static final int CHANNEL_OUT_13POINT_360RA = (
             CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3b9c05b..1a56915 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -390,6 +390,18 @@
      */
     @Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
 
+    /** @hide */
+    private static final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+            AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+            AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+            AudioManager.STREAM_DTMF,  AudioManager.STREAM_ACCESSIBILITY };
+
+    /** @hide */
+    @TestApi
+    public static final int[] getPublicStreamTypes() {
+        return PUBLIC_STREAM_TYPES;
+    }
+
     /**
      * Increase the ringer volume.
      *
@@ -922,8 +934,8 @@
     public void adjustStreamVolume(int streamType, int direction, int flags) {
         final IAudioService service = getService();
         try {
-            service.adjustStreamVolume(streamType, direction, flags,
-                    getContext().getOpPackageName());
+            service.adjustStreamVolumeWithAttribution(streamType, direction, flags,
+                    getContext().getOpPackageName(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -993,7 +1005,7 @@
         final IAudioService service = getService();
         try {
             service.setMasterMute(mute, flags, getContext().getOpPackageName(),
-                    UserHandle.getCallingUserId());
+                    UserHandle.getCallingUserId(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1075,6 +1087,7 @@
      * @return The minimum valid volume index for the stream.
      * @see #getStreamVolume(int)
      */
+    @TestApi
     public int getStreamMinVolumeInt(int streamType) {
         final IAudioService service = getService();
         try {
@@ -1245,7 +1258,8 @@
     public void setStreamVolume(int streamType, int index, int flags) {
         final IAudioService service = getService();
         try {
-            service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
+            service.setStreamVolumeWithAttribution(streamType, index, flags,
+                    getContext().getOpPackageName(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1270,7 +1284,7 @@
         final IAudioService service = getService();
         try {
             service.setVolumeIndexForAttributes(attr, index, flags,
-                                                getContext().getOpPackageName());
+                    getContext().getOpPackageName(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2730,7 +2744,7 @@
         final IAudioService service = getService();
         try {
             service.setMicrophoneMute(on, getContext().getOpPackageName(),
-                    UserHandle.getCallingUserId());
+                    UserHandle.getCallingUserId(), getContext().getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4257,7 +4271,9 @@
                         afr.getFocusGain(), mICallBack,
                         mAudioFocusDispatcher,
                         clientId,
-                        getContext().getOpPackageName() /* package name */, afr.getFlags(),
+                        getContext().getOpPackageName() /* package name */,
+                        getContext().getAttributionTag(),
+                        afr.getFlags(),
                         ap != null ? ap.cb() : null,
                         sdk);
             } catch (RemoteException e) {
@@ -4362,6 +4378,7 @@
                     durationHint, mICallBack, null,
                     AudioSystem.IN_VOICE_COMM_FOCUS_ID,
                     getContext().getOpPackageName(),
+                    getContext().getAttributionTag(),
                     AUDIOFOCUS_FLAG_LOCK,
                     null /* policy token */, 0 /* sdk n/a here*/);
         } catch (RemoteException e) {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8012f03..321db4e 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2051,7 +2051,8 @@
     };
 
     /** @hide */
-    public static String streamToString(int stream) {
+    @TestApi
+    public static @NonNull String streamToString(int stream) {
         if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
         if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
         return "UNKNOWN_STREAM_" + stream;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 23d9532..8822aea 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2082,8 +2082,9 @@
      * It may also be adjusted slightly for internal reasons.
      * If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
      * will be returned.
-     * <p>This method is only supported for PCM audio.
-     * It is not supported for compressed audio tracks.
+     * <p>This method is supported for PCM audio at all API levels.
+     * Compressed audio is supported in API levels 33 and above.
+     * For compressed streams the size of a frame is considered to be exactly one byte.
      *
      * @param bufferSizeInFrames requested buffer size in frames
      * @return the actual buffer size in frames or an error code,
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d746c85..41e443f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -82,7 +82,7 @@
  * Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF,
  * AVIF.
  * <p>
- * Supported for writing: JPEG, PNG, WebP.
+ * Supported for writing: JPEG, PNG, WebP, DNG.
  * <p>
  * Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
  * it. This class will search both locations for XMP data, but if XMP data exist both inside and
@@ -1294,6 +1294,7 @@
             new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
             new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
             new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
+            new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
             new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
@@ -1361,12 +1362,6 @@
             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
     };
 
-    // Tags for indicating the thumbnail offset and length
-    private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
-    private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
-
     // Mappings from tag number to tag name and each item represents one IFD tag group.
     private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
     // Mappings from tag name to tag number and each item represents one IFD tag group.
@@ -2078,7 +2073,7 @@
      * {@link #setAttribute(String,String)} to set all attributes to write and
      * make a single call rather than multiple calls for each attribute.
      * <p>
-     * This method is supported for JPEG, PNG and WebP files.
+     * This method is supported for JPEG, PNG, WebP, and DNG files.
      * <p class="note">
      * Note: after calling this method, any attempts to obtain range information
      * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
@@ -2091,13 +2086,17 @@
      */
     public void saveAttributes() throws IOException {
         if (!isSupportedFormatForSavingAttributes()) {
-            throw new IOException("ExifInterface only supports saving attributes on JPEG, PNG, "
-                    + "or WebP formats.");
+            throw new IOException("ExifInterface only supports saving attributes for JPEG, PNG, "
+                    + "WebP, and DNG formats.");
         }
         if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
             throw new IOException(
                     "ExifInterface does not support saving attributes for the current input.");
         }
+        if (mHasThumbnail && mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
+            throw new IOException("ExifInterface does not support saving attributes when the image "
+                    + "file has non-consecutive thumbnail strips");
+        }
 
         // Remember the fact that we've changed the file on disk from what was
         // originally parsed, meaning we can't answer range questions
@@ -2146,6 +2145,10 @@
                     savePngAttributes(bufferedIn, bufferedOut);
                 } else if (mMimeType == IMAGE_TYPE_WEBP) {
                     saveWebpAttributes(bufferedIn, bufferedOut);
+                } else if (mMimeType == IMAGE_TYPE_DNG || mMimeType == IMAGE_TYPE_UNKNOWN) {
+                    ByteOrderedDataOutputStream dataOutputStream =
+                            new ByteOrderedDataOutputStream(bufferedOut, ByteOrder.BIG_ENDIAN);
+                    writeExifSegment(dataOutputStream);
                 }
             }
         } catch (Exception e) {
@@ -2285,9 +2288,9 @@
             }
 
             ExifAttribute imageLengthAttribute =
-                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
+                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_LENGTH);
             ExifAttribute imageWidthAttribute =
-                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
+                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_WIDTH);
             if (imageLengthAttribute != null && imageWidthAttribute != null) {
                 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
                 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
@@ -2559,7 +2562,12 @@
         mIsInputStream = false;
         try {
             in = new FileInputStream(filename);
-            ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(in.getFD());
+            ParcelFileDescriptor modernFd;
+            try {
+                modernFd = FileUtils.convertToModernFd(in.getFD());
+            } catch (IOException e) {
+                modernFd = null;
+            }
             if (modernFd != null) {
                 closeQuietly(in);
                 in = new FileInputStream(modernFd.getFileDescriptor());
@@ -2927,10 +2935,12 @@
                     if (in.skipBytes(1) != 1) {
                         throw new IOException("Invalid SOFx");
                     }
-                    mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
-                            in.readUnsignedShort(), mExifByteOrder));
-                    mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
-                            in.readUnsignedShort(), mExifByteOrder));
+                    mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
+                                    ? TAG_IMAGE_LENGTH : TAG_THUMBNAIL_IMAGE_LENGTH,
+                            ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
+                    mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
+                                    ? TAG_IMAGE_WIDTH : TAG_THUMBNAIL_IMAGE_WIDTH,
+                            ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
                     length -= 5;
                     break;
                 }
@@ -4501,6 +4511,17 @@
         if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
             Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
         }
+
+        // TAG_THUMBNAIL_* tags should be replaced with TAG_* equivalents and vice versa if needed.
+        replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
+        replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
+        replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
+        replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
+        replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
+        replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
+        replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_ORIENTATION, TAG_THUMBNAIL_ORIENTATION);
+        replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_LENGTH, TAG_THUMBNAIL_IMAGE_LENGTH);
+        replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_WIDTH, TAG_THUMBNAIL_IMAGE_WIDTH);
     }
 
     /**
@@ -4581,8 +4602,15 @@
             removeAttribute(tag.name);
         }
         // Remove old thumbnail data
-        removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
-        removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
+        if (mHasThumbnail) {
+            if (mHasThumbnailStrips) {
+                removeAttribute(TAG_STRIP_OFFSETS);
+                removeAttribute(TAG_STRIP_BYTE_COUNTS);
+            } else {
+                removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT);
+                removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+            }
+        }
 
         // Remove null value tags.
         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
@@ -4609,10 +4637,17 @@
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (mHasThumbnail) {
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
-                    ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
+            if (mHasThumbnailStrips) {
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
+                        ExifAttribute.createUShort(0, mExifByteOrder));
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_BYTE_COUNTS,
+                        ExifAttribute.createUShort(mThumbnailLength, mExifByteOrder));
+            } else {
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
+                        ExifAttribute.createULong(0, mExifByteOrder));
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                        ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
+            }
         }
 
         // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
@@ -4641,8 +4676,13 @@
         }
         if (mHasThumbnail) {
             int thumbnailOffset = position;
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
-                    ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
+            if (mHasThumbnailStrips) {
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
+                        ExifAttribute.createUShort(thumbnailOffset, mExifByteOrder));
+            } else {
+                mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
+                        ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
+            }
             // Need to add mExifOffset, which is the offset to the EXIF data segment
             mThumbnailOffset = thumbnailOffset + mExifOffset;
             position += mThumbnailLength;
@@ -5206,9 +5246,20 @@
         }
     }
 
+    private void replaceInvalidTags(@IfdType int ifdType, String invalidTag, String validTag) {
+        if (!mAttributes[ifdType].isEmpty()) {
+            if (mAttributes[ifdType].get(invalidTag) != null) {
+                mAttributes[ifdType].put(validTag,
+                        mAttributes[ifdType].get(invalidTag));
+                mAttributes[ifdType].remove(invalidTag);
+            }
+        }
+    }
+
     private boolean isSupportedFormatForSavingAttributes() {
         if (mIsSupportedFile && (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_PNG
-                || mMimeType == IMAGE_TYPE_WEBP)) {
+                || mMimeType == IMAGE_TYPE_WEBP || mMimeType == IMAGE_TYPE_DNG
+                || mMimeType == IMAGE_TYPE_UNKNOWN)) {
             return true;
         }
         return false;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0b35ebe..b0c4a3b 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -77,15 +77,17 @@
     oneway void playerSessionId(in int piid, in int sessionId);
 
     // Java-only methods below.
-
-    oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
-            String callingPackage, String caller);
-
     void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
 
+    void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+            in String callingPackage, in String attributionTag);
+
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void setStreamVolume(int streamType, int index, int flags, String callingPackage);
 
+    void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+            in String callingPackage, in String attributionTag);
+
     oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
             String callingPackage, String caller);
 
@@ -95,7 +97,8 @@
 
     boolean isMasterMute();
 
-    void setMasterMute(boolean mute, int flags, String callingPackage, int userId);
+    void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+            in String attributionTag);
 
     @UnsupportedAppUsage
     int getStreamVolume(int streamType);
@@ -107,7 +110,8 @@
 
     List<AudioVolumeGroup> getAudioVolumeGroups();
 
-    void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags, String callingPackage);
+    void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
+            String callingPackage, in String attributionTag);
 
     int getVolumeIndexForAttributes(in AudioAttributes aa);
 
@@ -125,7 +129,7 @@
 
     boolean isMicrophoneMuted();
 
-    void setMicrophoneMute(boolean on, String callingPackage, int userId);
+    void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
 
     oneway void setMicrophoneMuteFromSwitch(boolean on);
 
@@ -186,8 +190,8 @@
     boolean isBluetoothA2dpOn();
 
     int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
-            IAudioPolicyCallback pcb, int sdk);
+            IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
+            in String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk);
 
     int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa,
             in String callingPackageName);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 1616c03..bac44ad 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -399,7 +399,7 @@
      * <p>The number and meaning of the planes in an Image are determined by the
      * format of the Image.</p>
      *
-     * <p>Once the Image has been closed, any access to the the plane's
+     * <p>Once the Image has been closed, any access to the plane's
      * ByteBuffer will fail.</p>
      *
      * @see #getFormat
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8b91536..85baa3d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1726,7 +1726,6 @@
     private static final int CB_ERROR = 3;
     private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
 
-
     private class EventHandler extends Handler {
         private MediaCodec mCodec;
 
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index a460954..5d7a8b3 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -18,7 +18,6 @@
 
 import android.util.Log;
 
-import android.media.MediaCodecInfo;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Map;
@@ -166,7 +165,8 @@
 
     /**
      * Create a list of media-codecs of a specific kind.
-     * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}.
+     * @param kind Either {@link MediaCodecList#REGULAR_CODECS} or
+     *             {@link MediaCodecList#ALL_CODECS}.
      */
     public MediaCodecList(int kind) {
         initCodecList();
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 3a5216e..6eb1af8 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -53,6 +53,7 @@
         public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume";
         public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event";
         public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode";
+        public static final String METRICS_MANAGER = "metrics" + SEPARATOR + "manager";
     }
 
     /**
@@ -120,10 +121,11 @@
                 createKey("gainDb", Double.class);
         public static final Key<String> GROUP =
                 createKey("group", String.class);
-        // For volume
-        public static final Key<Integer> INDEX = createKey("index", Integer.class);
-        public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class);
-        public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class);
+
+        public static final Key<Integer> INDEX = createKey("index", Integer.class); // volume
+        public static final Key<String> LOG_SESSION_ID = createKey("logSessionId", String.class);
+        public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class); // vol
+        public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class); // vol
         public static final Key<String> MODE =
                 createKey("mode", String.class); // audio_mode
         public static final Key<String> MUTE =
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba..ccd830a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.BIND_IMS_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -4308,7 +4307,7 @@
     @RequiresPermission(BIND_IMS_SERVICE)
     public void setOnRtpRxNoticeListener(
             @NonNull Context context,
-            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Executor executor,
             @NonNull OnRtpRxNoticeListener listener) {
         Objects.requireNonNull(context);
         Preconditions.checkArgument(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 7d80e93..77c1e55b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -171,8 +171,12 @@
     }
 
     /**
+     *
      * Sets the {@link LogSessionId} for MediaRecorder.
      *
+     * <p>The log session ID is a random 32-byte hexadecimal string that is used for monitoring the
+     * MediaRecorder performance.</p>
+     *
      * @param id the global ID for monitoring the MediaRecorder performance
      */
     public void setLogSessionId(@NonNull LogSessionId id) {
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 345d9b2..4de63f9 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2034,8 +2034,8 @@
         public void requestSetVolume(int volume) {
             if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
                 try {
-                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
-                            ActivityThread.currentPackageName());
+                    sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+                            ActivityThread.currentPackageName(), null);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error setting local stream volume", e);
                 }
@@ -2053,8 +2053,8 @@
                 try {
                     final int volume =
                             Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
-                    sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
-                            ActivityThread.currentPackageName());
+                    sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+                            ActivityThread.currentPackageName(), null);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error setting local stream volume", e);
                 }
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 0c73348..d00e5b5 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -113,12 +113,10 @@
      */
     public static final int MIX_TYPE_INVALID = -1;
     /**
-     * @hide
      * Mix type indicating playback streams are mixed.
      */
     public static final int MIX_TYPE_PLAYERS = 0;
     /**
-     * @hide
      * Mix type indicating recording streams are mixed.
      */
     public static final int MIX_TYPE_RECORDERS = 1;
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index abbcc66..99ddff2 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -16,6 +16,9 @@
 
 package android.media.audiopolicy;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +27,7 @@
 import android.os.Parcel;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Objects;
@@ -199,7 +203,19 @@
     }
 
     private final int mTargetMixType;
-    int getTargetMixType() { return mTargetMixType; }
+
+    /** @hide */
+    @IntDef({AudioMix.MIX_TYPE_PLAYERS, AudioMix.MIX_TYPE_RECORDERS})
+    @Retention(SOURCE)
+    public @interface MixType {}
+
+    /**
+     * Gets target mix type of the mixing rule.
+     */
+    public @MixType int getTargetMixType() {
+        return mTargetMixType;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private final ArrayList<AudioMixMatchCriterion> mCriteria;
     /** @hide */
@@ -469,17 +485,24 @@
         }
 
         /**
-         * Set target mix type of the mixing rule.
+         * Sets target mix type of the mixing rule.
          *
-         * <p>Note: If the mix type was not specified, it will be decided automatically by mixing
-         * rule. For {@link #RULE_MATCH_UID}, the default type is {@link AudioMix#MIX_TYPE_PLAYERS}.
+         * <p>Note: If the mix type was not specified, it will be decided automatically by matched
+         * mixing rule. For example, {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or {@link
+         * AudioMixingRule#RULE_MATCH_USERID} applied {@link AudioMix#MIX_TYPE_PLAYERS}, {@link
+         * AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} applied {@link
+         * AudioMix#MIX_TYPE_RECORDERS}. For {@link AudioMixingRule#RULE_MATCH_UID}, the mix type
+         * could be {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}, and
+         * {@link AudioMix#MIX_TYPE_PLAYERS} is the default value.
          *
          * @param mixType {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}
          * @return the same Builder instance.
-         *
-         * @hide
          */
-        public @NonNull Builder setTargetMixType(int mixType) {
+        public @NonNull Builder setTargetMixType(@MixType int mixType) {
+            if (mixType != AudioMix.MIX_TYPE_PLAYERS && mixType != AudioMix.MIX_TYPE_RECORDERS) {
+                throw new IllegalArgumentException("Illegal argument for mix type");
+            }
+
             mTargetMixType = mixType;
             Log.i("AudioMixingRule", "Builder setTargetMixType " + mixType);
             return this;
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index fca3498..31d5967 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.media.MediaRecorder;
@@ -240,6 +241,7 @@
      * @return the legacy stream type relevant for the given {@link AudioAttributes}.
      *         If none is found, it return DEFAULT stream type.
      */
+    @TestApi
     public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
         Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -273,6 +275,7 @@
      * @return the volume group id relevant for the given streamType.
      *         If none is found, {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} is returned.
      */
+    @TestApi
     public int getVolumeGroupIdForLegacyStreamType(int streamType) {
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsStreamType(streamType)) {
@@ -288,6 +291,7 @@
      * @return the volume group id associated with the given audio attributes if found,
      *         {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} otherwise.
      */
+    @TestApi
     public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
         Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -352,11 +356,19 @@
      * @hide
      * Default attributes, with default source to be aligned with native.
      */
-    public static final @NonNull AudioAttributes sDefaultAttributes =
+    private static final @NonNull AudioAttributes DEFAULT_ATTRIBUTES =
             new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
                                          .build();
 
     /**
+     * @hide
+     */
+    @TestApi
+    public static @NonNull AudioAttributes getDefaultAttributes() {
+        return DEFAULT_ATTRIBUTES;
+    }
+
+    /**
      * To avoid duplicating the logic in java and native, we shall make use of
      * native API native_get_product_strategies_from_audio_attributes
      * Keep in sync with frameworks/av/media/libaudioclient/AudioProductStrategy::attributesMatches
@@ -369,7 +381,7 @@
         Preconditions.checkNotNull(attr, "attr must not be null");
         String refFormattedTags = TextUtils.join(";", refAttr.getTags());
         String cliFormattedTags = TextUtils.join(";", attr.getTags());
-        if (refAttr.equals(sDefaultAttributes)) {
+        if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
             return false;
         }
         return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6b329f8..b1baf94 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -23,6 +23,8 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -39,6 +41,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Xml;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -99,6 +102,7 @@
     private int mSurfaceWidth;
     private int mSurfaceHeight;
     private final AttributeSet mAttrs;
+    private final XmlResourceParser mParser;
     private final int mDefStyleAttr;
     private int mWindowZOrder;
     private boolean mUseRequestedSurfaceLayout;
@@ -168,7 +172,16 @@
 
     public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mAttrs = attrs;
+        int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+        if (sourceResId != Resources.ID_NULL) {
+            Log.d(TAG, "Build local AttributeSet");
+            mParser  = context.getResources().getXml(sourceResId);
+            mAttrs = Xml.asAttributeSet(mParser);
+        } else {
+            Log.d(TAG, "Use passed in AttributeSet");
+            mParser = null;
+            mAttrs = attrs;
+        }
         mDefStyleAttr = defStyleAttr;
         resetSurfaceView();
         mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ee70714..e8ef464 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -46,6 +46,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -112,21 +113,34 @@
     /**
      * All the info about a connection.
      */
-    private class ConnectionRecord implements IBinder.DeathRecipient {
-        String pkg;
-        int uid;
-        int pid;
-        Bundle rootHints;
-        IMediaBrowserServiceCallbacks callbacks;
-        BrowserRoot root;
-        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+    private static class ConnectionRecord implements IBinder.DeathRecipient {
+        public final MediaBrowserService service;
+        public final String pkg;
+        public final int pid;
+        public final int uid;
+        public final Bundle rootHints;
+        public final IMediaBrowserServiceCallbacks callbacks;
+        public final BrowserRoot root;
+        public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+
+        ConnectionRecord(
+                MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
+                IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
+            this.service = service;
+            this.pkg = pkg;
+            this.pid = pid;
+            this.uid = uid;
+            this.rootHints = rootHints;
+            this.callbacks = callbacks;
+            this.root = root;
+        }
 
         @Override
         public void binderDied() {
-            mHandler.post(new Runnable() {
+            service.mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mConnections.remove(callbacks.asBinder());
+                    service.mConnections.remove(callbacks.asBinder());
                 }
             });
         }
@@ -199,39 +213,46 @@
         }
     }
 
-    private class ServiceBinder extends IMediaBrowserService.Stub {
+    private static class ServiceBinder extends IMediaBrowserService.Stub {
+        private WeakReference<MediaBrowserService> mService;
+
+        private ServiceBinder(MediaBrowserService service) {
+            mService = new WeakReference(service);
+        }
+
         @Override
         public void connect(final String pkg, final Bundle rootHints,
                 final IMediaBrowserServiceCallbacks callbacks) {
+            MediaBrowserService service = mService.get();
+            if (service == null) {
+                return;
+            }
 
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
-            if (!isValidPackage(pkg, uid)) {
+            if (!service.isValidPackage(pkg, uid)) {
                 throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
                         + " package=" + pkg);
             }
 
-            mHandler.post(new Runnable() {
+            service.mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         final IBinder b = callbacks.asBinder();
 
                         // Clear out the old subscriptions. We are getting new ones.
-                        mConnections.remove(b);
+                        service.mConnections.remove(b);
 
-                        final ConnectionRecord connection = new ConnectionRecord();
-                        connection.pkg = pkg;
-                        connection.pid = pid;
-                        connection.uid = uid;
-                        connection.rootHints = rootHints;
-                        connection.callbacks = callbacks;
-
-                        mCurConnection = connection;
-                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
-                        mCurConnection = null;
+                        // Temporarily sets a placeholder ConnectionRecord to make
+                        // getCurrentBrowserInfo() work in onGetRoot().
+                        service.mCurConnection =
+                                new ConnectionRecord(
+                                        service, pkg, pid, uid, rootHints, callbacks, null);
+                        BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
+                        service.mCurConnection = null;
 
                         // If they didn't return something, don't allow this client.
-                        if (connection.root == null) {
+                        if (root == null) {
                             Log.i(TAG, "No root for client " + pkg + " from service "
                                     + getClass().getName());
                             try {
@@ -242,16 +263,19 @@
                             }
                         } else {
                             try {
-                                mConnections.put(b, connection);
+                                ConnectionRecord connection =
+                                        new ConnectionRecord(
+                                                service, pkg, pid, uid, rootHints, callbacks, root);
+                                service.mConnections.put(b, connection);
                                 b.linkToDeath(connection, 0);
-                                if (mSession != null) {
+                                if (service.mSession != null) {
                                     callbacks.onConnect(connection.root.getRootId(),
-                                            mSession, connection.root.getExtras());
+                                            service.mSession, connection.root.getExtras());
                                 }
                             } catch (RemoteException ex) {
                                 Log.w(TAG, "Calling onConnect() failed. Dropping client. "
                                         + "pkg=" + pkg);
-                                mConnections.remove(b);
+                                service.mConnections.remove(b);
                             }
                         }
                     }
@@ -260,13 +284,18 @@
 
         @Override
         public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
-            mHandler.post(new Runnable() {
+            MediaBrowserService service = mService.get();
+            if (service == null) {
+                return;
+            }
+
+            service.mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         final IBinder b = callbacks.asBinder();
 
                         // Clear out the old subscriptions. We are getting new ones.
-                        final ConnectionRecord old = mConnections.remove(b);
+                        final ConnectionRecord old = service.mConnections.remove(b);
                         if (old != null) {
                             // TODO
                             old.callbacks.asBinder().unlinkToDeath(old, 0);
@@ -283,20 +312,25 @@
         @Override
         public void addSubscription(final String id, final IBinder token, final Bundle options,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            mHandler.post(new Runnable() {
+            MediaBrowserService service = mService.get();
+            if (service == null) {
+                return;
+            }
+
+            service.mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         final IBinder b = callbacks.asBinder();
 
                         // Get the record for the connection
-                        final ConnectionRecord connection = mConnections.get(b);
+                        final ConnectionRecord connection = service.mConnections.get(b);
                         if (connection == null) {
                             Log.w(TAG, "addSubscription for callback that isn't registered id="
                                     + id);
                             return;
                         }
 
-                        MediaBrowserService.this.addSubscription(id, connection, token, options);
+                        service.addSubscription(id, connection, token, options);
                     }
                 });
         }
@@ -310,18 +344,23 @@
         @Override
         public void removeSubscription(final String id, final IBinder token,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            mHandler.post(new Runnable() {
+            MediaBrowserService service = mService.get();
+            if (service == null) {
+                return;
+            }
+
+            service.mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     final IBinder b = callbacks.asBinder();
 
-                    ConnectionRecord connection = mConnections.get(b);
+                    ConnectionRecord connection = service.mConnections.get(b);
                     if (connection == null) {
                         Log.w(TAG, "removeSubscription for callback that isn't registered id="
                                 + id);
                         return;
                     }
-                    if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
+                    if (!service.removeSubscription(id, connection, token)) {
                         Log.w(TAG, "removeSubscription called for " + id
                                 + " which is not subscribed");
                     }
@@ -332,16 +371,21 @@
         @Override
         public void getMediaItem(final String mediaId, final ResultReceiver receiver,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            mHandler.post(new Runnable() {
+            MediaBrowserService service = mService.get();
+            if (service == null) {
+                return;
+            }
+
+            service.mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     final IBinder b = callbacks.asBinder();
-                    ConnectionRecord connection = mConnections.get(b);
+                    ConnectionRecord connection = service.mConnections.get(b);
                     if (connection == null) {
                         Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
                         return;
                     }
-                    performLoadItem(mediaId, connection, receiver);
+                    service.performLoadItem(mediaId, connection, receiver);
                 }
             });
         }
@@ -350,7 +394,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mBinder = new ServiceBinder();
+        mBinder = new ServiceBinder(this);
     }
 
     @Override
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 7c5f58e..116237f 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -282,7 +282,6 @@
     return status;
 }
 
-
 status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
     return mImpl->getSampleMeta(sampleMeta);
 }
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 7919723..a00031a 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -20,16 +20,15 @@
 import android.filterfw.core.FilterContext;
 import android.filterfw.core.Frame;
 import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GLFrame;
 import android.filterfw.core.GenerateFieldPort;
 import android.filterfw.core.GenerateFinalPort;
-import android.filterfw.core.GLFrame;
 import android.filterfw.core.MutableFrameFormat;
 import android.filterfw.core.ShaderProgram;
 import android.filterfw.format.ImageFormat;
 import android.graphics.SurfaceTexture;
-import android.os.ConditionVariable;
 import android.opengl.Matrix;
-
+import android.os.ConditionVariable;
 import android.util.Log;
 
 /** <p>A filter that converts textures from a SurfaceTexture object into frames for
@@ -57,7 +56,7 @@
         public void onSurfaceTextureSourceReady(SurfaceTexture source);
     }
     /** A callback to send the internal SurfaceTexture object to, once it is
-     * created. This callback will be called when the the filter graph is
+     * created. This callback will be called when the filter graph is
      * preparing to execute, but before any processing has actually taken
      * place. The SurfaceTexture object passed to this callback is the only way
      * to feed this filter. When the filter graph is shutting down, this
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index 1131c62..27cf943 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -255,7 +255,7 @@
         AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
         mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
 
-        final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+        final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes());
         try {
             // Validate Audio Volume Groups callback reception
             for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
index c0f596b..0e918d1 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotEquals;
 
 import android.media.AudioAttributes;
+import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.audiopolicy.AudioProductStrategy;
 import android.media.audiopolicy.AudioVolumeGroup;
@@ -71,7 +72,7 @@
         assertNotNull(audioProductStrategies);
         assertTrue(audioProductStrategies.size() > 0);
 
-        for (final int streamType : PUBLIC_STREAM_TYPES) {
+        for (final int streamType : AudioManager.getPublicStreamTypes()) {
             AudioAttributes aaFromStreamType =
                     AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
                             streamType);
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
index a17d65c..b30ef30 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -37,15 +37,10 @@
     // Default matches the invalid (empty) attributes from native.
     // The difference is the input source default which is not aligned between native and java
     public static final AudioAttributes sDefaultAttributes =
-            AudioProductStrategy.sDefaultAttributes;
+            AudioProductStrategy.getDefaultAttributes();
 
     public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
 
-    public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
-            AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
-            AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
-            AudioManager.STREAM_DTMF,  AudioManager.STREAM_ACCESSIBILITY };
-
     public AudioVolumesTestBase() {
         super("com.android.audiopolicytest", AudioPolicyTest.class);
     }
@@ -63,7 +58,7 @@
             }
             AudioAttributes avgAttributes = sDefaultAttributes;
             for (final AudioAttributes aa : avg.getAudioAttributes()) {
-                if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
                     avgAttributes = aa;
                     break;
                 }
@@ -89,7 +84,7 @@
                     assertTrue(!avg.getAudioAttributes().isEmpty());
                     AudioAttributes avgAttributes = sDefaultAttributes;
                     for (final AudioAttributes aa : avg.getAudioAttributes()) {
-                        if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                        if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
                             avgAttributes = aa;
                             break;
                         }
@@ -114,7 +109,7 @@
 
         // Store the original volumes that that they can be recovered in tearDown().
         mOriginalStreamVolumes.clear();
-        for (int streamType : PUBLIC_STREAM_TYPES) {
+        for (int streamType : AudioManager.getPublicStreamTypes()) {
             mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
         }
         // Store the original volume per attributes so that they can be recovered in tearDown()
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index cdf4851..3faed55 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index a03ea0df..99466d7 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; እንዲያስተዳድር ያቀናብሩት"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
+    <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 970c46b..c3f1e73 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"‏اضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
-    <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
+    <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 477844c..2a2bc25 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ছেট কৰক - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
-    <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"আপোনাৰ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্‌টোৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
+    <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index f10c639..2ec13c5 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə olunmasını ayarlayın - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
+    <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index e8542f3..d687b05 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 13be6f2..2236052f5 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3bda5e6..996ca90 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index d3bc515..16d25ce 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -19,9 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
     <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
-    <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; সেট করুন"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"না থাক"</string>
+    <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
+    <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 905b306..10f753c 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 86dc694..e55a033 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar el teu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
+    <string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 389ccd0..48fbda1 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 5a31f9b..446c301 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
+    <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Angiv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index b643eb2..33d831d 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 60de2ff..7a78c06 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index f3c4b1d..6cc56a4 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎Set &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎Yes‎‏‎‎‏‎"</string>
-    <string name="consent_no" msgid="1335543792857823917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎No thanks‎‏‎‎‏‎"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
+    <string name="consent_no" msgid="2640796915611404382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎Don’t allow‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 4fbb57e..dc13159 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 5ca9305..7e37c1d 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Haz que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 357f052..399556d 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 14c7154..764505e 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kudea dezan"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ez"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 6bb9620..07d04aa 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‏&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
-    <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
+    <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 5a9c1cd..528d16c 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Aseta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index b31babd..1dcd337 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 08c93a2c..ba2fc8e 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index c95b90e..5f9a8d7 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 7e41042..71cf012 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
+    <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index ac95cc6..d4dd1cb 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; को प्रबंधित करने के लिए, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को सेट करें"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
-    <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> को मैनेज करने के लिए, यह ऐप्लिकेशन ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
+    <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index df8451f..87c5ae2 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index ff1c6c5..c7ceb38 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kezelésére"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Igen"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nem"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 194223d..26f7990 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է <xliff:g id="PROFILE_NAME">%1$s</xliff:g> սարքը կամ պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 58bf3cb..b0618d4 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Tidak"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index cc5b989..b7d7c6a 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 4cbefd8..ce003e7 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sì"</string>
-    <string name="consent_no" msgid="1335543792857823917">"No, grazie"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8663e56..54c523c 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,11 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
-    <string name="chooser_title" msgid="2262294130493605839">"‏בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="chooser_title" msgid="2262294130493605839">"‏בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
-    <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את ה<xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
+    <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index ca17336..f92fafe 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
-    <string name="consent_no" msgid="1335543792857823917">"いいえ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"このアプリは <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"許可"</string>
+    <string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 300c94f..34efdd2 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
+    <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 94d6c3e..3c7f697 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index db13fe7..74ccd84 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
+    <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 0225166..2a82d1f 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 1363e57..6ca01bf 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)을(를) 관리하도록 설정"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"예"</string>
-    <string name="consent_no" msgid="1335543792857823917">"취소"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"허용"</string>
+    <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index c01e235..18d38a8 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарсын"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 68218dd..a1eb713 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 5fd8280..65f371d 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -19,9 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
-    <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; būtų valdomas programos &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string>
+    <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index bf036ec..b18bfe4 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) pārvaldībai"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 427ca8f..9d745c4 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a48c45f..28c88da 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
-    <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
+    <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 7ac20e6..11e61d9 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 144698b..c73ed28 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,9 +19,9 @@
     <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
-    <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
-    <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
+    <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
+    <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 7bea2c9..d2aebb4 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 9c2783c..45c9d8b 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သတ်မှတ်ပါ"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
-    <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 26fbb03..af1ffe9 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Angi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index f289b37..b29f94c 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string>
+    <string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string>
     <string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्न &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; तोक्नुहोस्"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"तपाईंको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एपलाई अनुमति दिनु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
+    <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 0c9cdffd..a56fb9a 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te beheren"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index c8c680f..8e43213 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 0da9410..54f4f8c 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index b07af57..a989baa 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Tak"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 16906f6..d2724c0 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 745d163..2f5a53b 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 16906f6..d2724c0 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 187cfbdf..4df74de 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 8dd9a39..ea372d5 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Нет"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 9e7c02e..a5c2c88 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
-    <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55a47c2..a9bf77f 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 159afd5..4eb8f50 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 6fa759c..34357b4 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index fdbbe8e..37af185 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index bfd2516..f78fadf 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Nej tack"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 437ae7f..495c441 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Hapana"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 9b4a720..20845bd 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அமையுங்கள்"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
-    <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
+    <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 6e785de..c855cf2 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను సెటప్ చేయండి"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
-    <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
+    <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index b727d42..d78ada2 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
-    <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
+    <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index a93282a..03165b4 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 3abe064..b2c1cf2 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazınızı yönetecek şekilde ayarlayın"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
+    <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 161d95e..61b78e9 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати своїм пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index dce1815..ee79921 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
-    <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
+    <string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 2ca27b5..7221b6d 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 06a1ab6..2819e1d 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 12bfcf3..1440c40 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"设为由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"好"</string>
-    <string name="consent_no" msgid="1335543792857823917">"不用了"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"需要使用此应用,才能管理您的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"允许"</string>
+    <string name="consent_no" msgid="2640796915611404382">"不允许"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 0c583b2..e3f1eb1 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+    <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
     <string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"是"</string>
-    <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+    <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 519f0e8..9f4041d 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"是"</string>
-    <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"需使用這個應用程式,才能管理「<xliff:g id="PROFILE_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+    <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 7721b54..dc933ae 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -20,8 +20,8 @@
     <string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
-    <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
+    <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
+    <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
+    <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
 </resources>
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
new file mode 100644
index 0000000..eb1faa0
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Type annotations for constants used in the connectivity API surface.
+ *
+ * The annotations are maintained in a separate class so that it can be built as
+ * a separate library that other modules can build against, as Typedef should not
+ * be exposed as SystemApi.
+ *
+ * @hide
+ */
+public final class ConnectivityAnnotations {
+    private ConnectivityAnnotations() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
+            ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
+            ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
+    })
+    public @interface MultipathPreference {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, value = {
+            ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
+            ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+            ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
+    })
+    public @interface RestrictBackgroundStatus {}
+}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 48cdf16..197b7b2 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -31,7 +31,7 @@
             android:directBootAware="true">
 
         <receiver android:name=".TemporaryFileManager"
-            android:exported="true">
+            android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
@@ -76,7 +76,7 @@
 
         <receiver android:name=".InstallEventReceiver"
                 android:permission="android.permission.INSTALL_PACKAGES"
-                android:exported="true">
+                android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
             </intent-filter>
@@ -106,14 +106,14 @@
 
         <receiver android:name=".UninstallEventReceiver"
             android:permission="android.permission.INSTALL_PACKAGES"
-            android:exported="true">
+            android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="com.android.packageinstaller.ACTION_UNINSTALL_COMMIT" />
             </intent-filter>
         </receiver>
 
         <receiver android:name=".PackageInstalledReceiver"
-                android:exported="true">
+                android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="android.intent.action.PACKAGE_ADDED" />
                 <data android:scheme="package" />
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5d7b9bb..cef9014 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -19,6 +19,9 @@
     },
     {
       "name": "CtsPackageUninstallTestCases"
+    },
+    {
+      "name": "PackageInstallerTests"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index 800ab67..4a07d49 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -19,5 +19,5 @@
     ],
 
     sdk_version: "system_current",
-    min_sdk_version: "21",
+    min_sdk_version: "28",
 }
diff --git a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml b/packages/SettingsLib/ActionBarShadow/lint-baseline.xml
deleted file mode 100644
index 4d5de5f..0000000
--- a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`"
-        errorLine1="            mScrollView.setOnScrollChangeListener(mScrollChangeWatcher);"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="81"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`"
-        errorLine1="        mScrollView.setOnScrollChangeListener(null);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="88"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 23 (current min is 21): `android.view.View.OnScrollChangeListener`"
-        errorLine1="    final class ScrollChangeWatcher implements View.OnScrollChangeListener {"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="95"
-            column="48"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
deleted file mode 100644
index 95b7e3b..0000000
--- a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`android:Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)"
-        errorLine1="    &lt;style name=&quot;SettingsActionButton&quot; parent=&quot;android:Widget.DeviceDefault.Button.Borderless.Colored&quot;>"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml"
-            line="19"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`android:drawableTint` requires API level 23 (current min is 21)"
-        errorLine1="        &lt;item name=&quot;android:drawableTint&quot;>@*android:color/btn_colored_borderless_text_material&lt;/item>"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml"
-            line="21"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
-            line="23"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
-            line="25"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
-            line="24"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
-            line="26"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml"
-            line="23"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 16a85d6..8a25726 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -17,6 +17,8 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 1b9f68f..7e626e5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -1,22 +1,24 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT 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"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index a884ef1..9f4980b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -1,22 +1,24 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT 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"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
index 42c7d76..8a449cf 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -15,7 +15,9 @@
   limitations under the License.
   -->
 
-<resources>
+<resources
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="28">
     <style name="SettingsLibActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
         <item name="android:drawablePadding">4dp</item>
         <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index a65bf41..2b8f049 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -11,33 +11,6 @@
 
     name: "SettingsLib",
 
-    defaults: [
-        "SettingsLibDependenciesWithoutWifiTracker",
-    ],
-
-    // TODO(b/149540986): revert this change.
-    static_libs: [
-        // All other dependent components should be put in
-        // "SettingsLibDependenciesWithoutWifiTracker".
-        "WifiTrackerLib",
-    ],
-
-    // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
-    // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
-
-    resource_dirs: ["res"],
-
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-    ],
-
-    min_sdk_version: "29",
-
-}
-
-java_defaults {
-    name: "SettingsLibDependenciesWithoutWifiTracker",
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.legacy_legacy-support-v4",
@@ -48,6 +21,7 @@
         "androidx.mediarouter_mediarouter-nodeps",
         "iconloader",
 
+        "WifiTrackerLibRes",
         "SettingsLibHelpUtils",
         "SettingsLibRestrictedLockUtils",
         "SettingsLibActionBarShadow",
@@ -74,6 +48,19 @@
         "SettingsLibTwoTargetPreference",
         "SettingsLibSettingsTransition",
     ],
+
+    // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
+    // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
+
+    resource_dirs: ["res"],
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    min_sdk_version: "29",
+
 }
 
 // NOTE: Keep this module in sync with ./common.mk
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
new file mode 100644
index 0000000..d7e778f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Weier"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
new file mode 100644
index 0000000..6701dea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"አሰናብት"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
new file mode 100644
index 0000000..0f1b9ac
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"إغلاق"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
new file mode 100644
index 0000000..21dd94c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"অগ্ৰাহ্য কৰক"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
new file mode 100644
index 0000000..7f91eb4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Qapadın"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..ca16c3d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
new file mode 100644
index 0000000..b0980ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Адхіліць"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
new file mode 100644
index 0000000..cccbf96
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отхвърляне"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
new file mode 100644
index 0000000..e0dfcf2
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
new file mode 100644
index 0000000..5e46c6c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacivanje"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
new file mode 100644
index 0000000..81bb048
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
new file mode 100644
index 0000000..ac7623e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavřít"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
new file mode 100644
index 0000000..8c185d9
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Luk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
new file mode 100644
index 0000000..006301b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
new file mode 100644
index 0000000..65843b2
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Παράβλεψη"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..418c1d5
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..418c1d5
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..418c1d5
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..418c1d5
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..e2dae5e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‎Dismiss‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..4816be6
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Descartar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
new file mode 100644
index 0000000..5e820238
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cerrar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
new file mode 100644
index 0000000..a688723
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Loobu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
new file mode 100644
index 0000000..64dd1c5
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Baztertu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
new file mode 100644
index 0000000..bd8985f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"رد شدن"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
new file mode 100644
index 0000000..c384157
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ohita"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..dd5889c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
new file mode 100644
index 0000000..dd5889c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
new file mode 100644
index 0000000..d787626
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
new file mode 100644
index 0000000..1fe4c5c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"છોડી દો"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
new file mode 100644
index 0000000..f66ee7f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"खारिज करें"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
new file mode 100644
index 0000000..f7e7cd0
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbaci"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
new file mode 100644
index 0000000..1551c84
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Bezárás"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
new file mode 100644
index 0000000..e014cce
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Փակել"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
new file mode 100644
index 0000000..607e811
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Tutup"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
new file mode 100644
index 0000000..4afc614
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hunsa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
new file mode 100644
index 0000000..81bb048
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
new file mode 100644
index 0000000..aa4c669
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"סגירה"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
new file mode 100644
index 0000000..b42f6e6
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"閉じる"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
new file mode 100644
index 0000000..7bde8b6
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"უარყოფა"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
new file mode 100644
index 0000000..01235e0
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
new file mode 100644
index 0000000..4e14820
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ច្រានចោល"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
new file mode 100644
index 0000000..b9a5420
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ವಜಾಗೊಳಿಸಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
new file mode 100644
index 0000000..9b51699
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"닫기"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
new file mode 100644
index 0000000..affb8ec
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабуу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
new file mode 100644
index 0000000..7079f7c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ປິດໄວ້"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
new file mode 100644
index 0000000..4cee14a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Atsisakyti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
new file mode 100644
index 0000000..120a762
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Nerādīt"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
new file mode 100644
index 0000000..76a4390
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отфрли"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
new file mode 100644
index 0000000..5a4e14c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
new file mode 100644
index 0000000..3974470
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Үл хэрэгсэх"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
new file mode 100644
index 0000000..4bd4485
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
new file mode 100644
index 0000000..290323b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ketepikan"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
new file mode 100644
index 0000000..52ecc49
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ပယ်ရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
new file mode 100644
index 0000000..c1e39a4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Lukk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
new file mode 100644
index 0000000..1510254
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
new file mode 100644
index 0000000..920349f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Sluiten"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
new file mode 100644
index 0000000..36e7d3b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
new file mode 100644
index 0000000..250ef2e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ਖਾਰਜ ਕਰੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
new file mode 100644
index 0000000..9ad630a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zamknij"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..80b70ae
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..d787626
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
new file mode 100644
index 0000000..80b70ae
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
new file mode 100644
index 0000000..18b6a0e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
new file mode 100644
index 0000000..b694657
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрыть"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
new file mode 100644
index 0000000..d818cf7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ඉවත ලන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
new file mode 100644
index 0000000..4f59f85
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavrieť"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
new file mode 100644
index 0000000..1ca68bf
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Opusti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
new file mode 100644
index 0000000..dbe7927
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hiq"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
new file mode 100644
index 0000000..68a2d5b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
new file mode 100644
index 0000000..ef2df3c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorera"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
new file mode 100644
index 0000000..ebb0c02
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ondoa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
new file mode 100644
index 0000000..9b175c7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"மூடும்"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
new file mode 100644
index 0000000..22a6f59
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"విస్మరించు"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
new file mode 100644
index 0000000..6546bfa
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ปิด"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
new file mode 100644
index 0000000..9b944de
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"I-dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
new file mode 100644
index 0000000..96d49e9
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Kapat"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
new file mode 100644
index 0000000..f51b0e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрити"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
new file mode 100644
index 0000000..ad3fafb
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"برخاست کریں"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
new file mode 100644
index 0000000..1e24745
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Yopish"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
new file mode 100644
index 0000000..a30cdbf
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Đóng"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..a8f36e4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"关闭"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..b9ee658
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..b9ee658
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
new file mode 100644
index 0000000..80faa17
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cashisa"</string>
+</resources>
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
index ae26066..4f65373 100644
--- a/packages/SettingsLib/BarChartPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -14,7 +14,7 @@
     resource_dirs: ["res"],
 
     static_libs: [
-          "androidx.preference_preference",
+        "androidx.preference_preference",
     ],
 
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/BarChartPreference/lint-baseline.xml b/packages/SettingsLib/BarChartPreference/lint-baseline.xml
deleted file mode 100644
index f1043bb..0000000
--- a/packages/SettingsLib/BarChartPreference/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`@android:style/Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)"
-        errorLine1="           parent=&quot;@android:style/Widget.DeviceDefault.Button.Borderless.Colored&quot;>"
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/BarChartPreference/res/values/styles.xml"
-            line="35"
-            column="12"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
index 7a3fb7d..92514ad 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -15,7 +15,9 @@
   limitations under the License.
   -->
 
-<resources>
+<resources
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="28">
 
     <style name="SettingsBarChart">
         <item name="android:layout_marginStart">10dp</item>
diff --git a/packages/SettingsLib/FooterPreference/res/values-af/strings.xml b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
new file mode 100644
index 0000000..c17f3ed
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Kom meer te wete"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-am/strings.xml b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
new file mode 100644
index 0000000..02e6131
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"የበለጠ ለመረዳት"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
new file mode 100644
index 0000000..1f279a6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"مزيد من المعلومات"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-as/strings.xml b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
new file mode 100644
index 0000000..a34b474
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"অধিক জানক"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-az/strings.xml b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
new file mode 100644
index 0000000..b49036e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Ətraflı məlumat"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..993ec9a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-be/strings.xml b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
new file mode 100644
index 0000000..f9d6129
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Даведацца больш"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
new file mode 100644
index 0000000..605663d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Научете повече"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
new file mode 100644
index 0000000..c58142d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
new file mode 100644
index 0000000..993ec9a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
new file mode 100644
index 0000000..7abf10f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Més informació"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
new file mode 100644
index 0000000..decbb68e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Další informace"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-da/strings.xml b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
new file mode 100644
index 0000000..81d1c7c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Få flere oplysninger"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
new file mode 100644
index 0000000..fe885aa
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-el/strings.xml b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
new file mode 100644
index 0000000..5a30833
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Μάθετε περισσότερα"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..924d735
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..924d735
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..924d735
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..924d735
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..bd12547
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎Learn more‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..f31d9ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
new file mode 100644
index 0000000..f31d9ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-et/strings.xml b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
new file mode 100644
index 0000000..78b65ed
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Lisateave"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
new file mode 100644
index 0000000..cf7fa00
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Lortu informazio gehiago"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
new file mode 100644
index 0000000..464c58e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"بیشتر بدانید"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
new file mode 100644
index 0000000..856b962
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Lue lisää"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..6d856ca
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
new file mode 100644
index 0000000..6d856ca
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
new file mode 100644
index 0000000..cde57d8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Máis información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
new file mode 100644
index 0000000..54249b8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"વધુ જાણો"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
new file mode 100644
index 0000000..95ae240
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ज़्यादा जानें"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
new file mode 100644
index 0000000..993ec9a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
new file mode 100644
index 0000000..ae3c948
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"További információ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
new file mode 100644
index 0000000..de9137b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Իմանալ ավելին"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-in/strings.xml b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
new file mode 100644
index 0000000..4b5cb16
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Pelajari lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-is/strings.xml b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
new file mode 100644
index 0000000..111094c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Nánar"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-it/strings.xml b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
new file mode 100644
index 0000000..053c80c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Scopri di più"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
new file mode 100644
index 0000000..55b0187
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"מידע נוסף"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
new file mode 100644
index 0000000..3312cb4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"詳細"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
new file mode 100644
index 0000000..67bb223
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"შეიტყვეთ მეტი"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
new file mode 100644
index 0000000..db11a76
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Толығырақ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-km/strings.xml b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
new file mode 100644
index 0000000..1977dd3
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ស្វែងយល់បន្ថែម"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
new file mode 100644
index 0000000..47fa3d5
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
new file mode 100644
index 0000000..d8d2200
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"자세히 알아보기"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
new file mode 100644
index 0000000..74c6a49
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Кеңири маалымат"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
new file mode 100644
index 0000000..2e4124b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ສຶກສາເພີ່ມເຕີມ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
new file mode 100644
index 0000000..2981c66
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Sužinokite daugiau"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
new file mode 100644
index 0000000..9766305
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Uzzināt vairāk"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
new file mode 100644
index 0000000..1f734c5
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Дознајте повеќе"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
new file mode 100644
index 0000000..1cd466b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"കൂടുതലറിയുക"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
new file mode 100644
index 0000000..8bac1eb
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Нэмэлт мэдээлэл авах"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
new file mode 100644
index 0000000..4538720
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
new file mode 100644
index 0000000..cd1b17a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Ketahui lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
new file mode 100644
index 0000000..751a87a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
new file mode 100644
index 0000000..08de009
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Finn ut mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
new file mode 100644
index 0000000..ecfec36
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
new file mode 100644
index 0000000..1564081
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Meer informatie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
new file mode 100644
index 0000000..e7924d6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
new file mode 100644
index 0000000..1ce2ef2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ਹੋਰ ਜਾਣੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
new file mode 100644
index 0000000..5709f3e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Więcej informacji"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..bc410ab
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..bc410ab
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
new file mode 100644
index 0000000..bc410ab
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
new file mode 100644
index 0000000..2b50117
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Aflați mai multe"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
new file mode 100644
index 0000000..bedde40
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Подробнее…"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-si/strings.xml b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
new file mode 100644
index 0000000..1a60601
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"තව දැන ගන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
new file mode 100644
index 0000000..c1008e5
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Ďalšie informácie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
new file mode 100644
index 0000000..79e0a73
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Več o tem"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
new file mode 100644
index 0000000..0fea476c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Mëso më shumë"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
new file mode 100644
index 0000000..9a73269
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
new file mode 100644
index 0000000..a78c3cb
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Läs mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
new file mode 100644
index 0000000..52b1732
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Pata maelezo zaidi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
new file mode 100644
index 0000000..75fc7c1
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"மேலும் அறிக"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-te/strings.xml b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
new file mode 100644
index 0000000..6c8d679
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"మరింత తెలుసుకోండి"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-th/strings.xml b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
new file mode 100644
index 0000000..025a2f0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"ดูข้อมูลเพิ่มเติม"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
new file mode 100644
index 0000000..4b6f830
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Matuto pa"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
new file mode 100644
index 0000000..77d15130
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Daha fazla bilgi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
new file mode 100644
index 0000000..cec933d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Докладніше"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
new file mode 100644
index 0000000..1dceea7
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"مزید جانیں"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
new file mode 100644
index 0000000..5823949
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Batafsil"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
new file mode 100644
index 0000000..d6c4638
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Tìm hiểu thêm"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..446c8ce
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"了解详情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..8ab38c6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..8ab38c6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
new file mode 100644
index 0000000..b53eb85
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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="settingslib_learn_more_text" msgid="7385478101223578464">"Funda kabanzi"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/lint-baseline.xml b/packages/SettingsLib/HelpUtils/lint-baseline.xml
deleted file mode 100644
index 940f027..0000000
--- a/packages/SettingsLib/HelpUtils/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 28 (current min is 21): `android.content.pm.PackageInfo#getLongVersionCode`"
-        errorLine1="                sCachedVersionCode = Long.toString(info.getLongVersionCode());"
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java"
-            line="239"
-            column="57"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 541a246..70c8658 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.Log;
@@ -32,6 +33,7 @@
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.widget.R;
@@ -73,6 +75,15 @@
     private HelpUtils() {
     }
 
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     * item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
             String backupContext) {
         // menu contains help item, skip it
@@ -84,6 +95,15 @@
         return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
     }
 
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     * item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
             String backupContext) {
         // menu contains help item, skip it
@@ -105,6 +125,7 @@
      * @return returns whether the help menu item has been made visible.
      */
     @VisibleForTesting
+    @RequiresApi(Build.VERSION_CODES.P)
     static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
             String helpUriString, String backupContext) {
         if (Global.getInt(activity.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
@@ -152,6 +173,10 @@
         }
     }
 
+    /**
+     * Get the help intent from helpUriString.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static Intent getHelpIntent(Context context, String helpUriString,
             String backupContext) {
         if (Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
@@ -223,7 +248,8 @@
      *
      * @return the uri with added query parameters
      */
-    private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+    @RequiresApi(Build.VERSION_CODES.P)
+    public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
         Uri.Builder builder = baseUri.buildUpon();
 
         // Add in the preferred language
diff --git a/packages/SettingsLib/ProgressBar/lint-baseline.xml b/packages/SettingsLib/ProgressBar/lint-baseline.xml
deleted file mode 100644
index 03d0f3f..0000000
--- a/packages/SettingsLib/ProgressBar/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/colorSecondary` requires API level 25 (current min is 21)"
-        errorLine1="        android:background=&quot;?android:attr/colorSecondary&quot; />"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml"
-            line="27"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
index 268858b..52ff42c 100644
--- a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
+++ b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
@@ -17,6 +17,8 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="25"
     android:layout_width="match_parent"
     android:layout_height="3dp">
     <View
diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
deleted file mode 100644
index 173c735..0000000
--- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`"
-        errorLine1="        ComponentName adminComponent = userContext.getSystemService("
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="59"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`"
-        errorLine1="        UserManager um = context.getSystemService(UserManager.class);"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="101"
-            column="34"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index a77e34b..80f02b4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -21,11 +21,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import java.util.Objects;
 
@@ -34,10 +36,18 @@
  * support message dialog.
  */
 public class RestrictedLockUtils {
+    /**
+     * Get EnforcedAdmin from DevicePolicyManager
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static EnforcedAdmin getProfileOrDeviceOwner(Context context, UserHandle user) {
         return getProfileOrDeviceOwner(context, null, user);
     }
 
+    /**
+     * Get EnforcedAdmin from DevicePolicyManager
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static EnforcedAdmin getProfileOrDeviceOwner(
             Context context, String enforcedRestriction, UserHandle user) {
         if (user == null) {
@@ -73,6 +83,7 @@
     /**
      * Send the intent to trigger the {@code android.settings.ShowAdminSupportDetailsDialog}.
      */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
         final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
         int targetUserId = UserHandle.myUserId();
@@ -97,6 +108,10 @@
         return intent;
     }
 
+    /**
+     * Check if current user is profile or not
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static boolean isCurrentUserOrProfile(Context context, int userId) {
         UserManager um = context.getSystemService(UserManager.class);
         return um.getUserProfiles().contains(UserHandle.of(userId));
diff --git a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml b/packages/SettingsLib/SettingsSpinner/lint-baseline.xml
deleted file mode 100644
index ae1ed38e..0000000
--- a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `new android.widget.Spinner`"
-        errorLine1="        super(context, attrs, defStyleAttr, defStyleRes, mode, null);"
-        errorLine2="        ~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java"
-            line="122"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
index 0be80a9f..14286fa 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
@@ -17,9 +17,12 @@
 package com.android.settingslib.widget.settingsspinner;
 
 import android.content.Context;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.widget.Spinner;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.settingslib.widget.R;
 
 /**
@@ -117,6 +120,7 @@
      * @see Spinner#MODE_DIALOG
      * @see Spinner#MODE_DROPDOWN
      */
+    @RequiresApi(Build.VERSION_CODES.M)
     public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
             int mode) {
         super(context, attrs, defStyleAttr, defStyleRes, mode, null);
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java b/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java
new file mode 100644
index 0000000..7c7044c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.utils;
+
+import android.os.Handler;
+
+/**
+ * Wrapper the {@link android.os.Handler} for testing compatibility.
+ */
+public class HandlerInjector {
+
+    protected final Handler mHandler;
+
+    public HandlerInjector(Handler handler) {
+        mHandler = handler;
+    }
+
+    /**  Wrapper the {@link android.os.Handler#postDelayed} for testing compatibility. */
+    public void postDelayed(Runnable runnable, long delayMillis) {
+        mHandler.postDelayed(runnable, delayMillis);
+    }
+
+    /**  Wrapper the {@link android.os.Handler#removeCallbacks} for testing compatibility. */
+    public void removeCallbacks(Runnable runnable) {
+        mHandler.removeCallbacks(runnable);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
deleted file mode 100644
index 503d60c..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
+++ /dev/null
@@ -1,46 +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.settingslib.wifi;
-
-import android.content.Context;
-
-import androidx.fragment.app.Fragment;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * WifiEntryPreference that can be long pressed.
- */
-public class LongPressWifiEntryPreference extends WifiEntryPreference {
-
-    private final Fragment mFragment;
-
-    public LongPressWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment) {
-        super(context, wifiEntry);
-        mFragment = fragment;
-    }
-
-    @Override
-    public void onBindViewHolder(final PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-        if (mFragment != null) {
-            view.itemView.setOnCreateContextMenuListener(mFragment);
-            view.itemView.setTag(this);
-            view.itemView.setLongClickable(true);
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
deleted file mode 100644
index 4dd3ff1..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ /dev/null
@@ -1,314 +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.settingslib.wifi;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.settingslib.Utils;
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * Preference to display a WifiEntry in a wifi picker.
- */
-public class WifiEntryPreference extends Preference implements WifiEntry.WifiEntryCallback,
-        View.OnClickListener {
-
-    private static final int[] STATE_SECURED = {
-            R.attr.state_encrypted
-    };
-
-    private static final int[] FRICTION_ATTRS = {
-            R.attr.wifi_friction
-    };
-
-    // These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX]
-    private static final int[] WIFI_CONNECTION_STRENGTH = {
-            R.string.accessibility_no_wifi,
-            R.string.accessibility_wifi_one_bar,
-            R.string.accessibility_wifi_two_bars,
-            R.string.accessibility_wifi_three_bars,
-            R.string.accessibility_wifi_signal_full
-    };
-
-    // StateListDrawable to display secured lock / metered "$" icon
-    @Nullable private final StateListDrawable mFrictionSld;
-    private final IconInjector mIconInjector;
-    private WifiEntry mWifiEntry;
-    private int mLevel = -1;
-    private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
-    private CharSequence mContentDescription;
-    private OnButtonClickListener mOnButtonClickListener;
-
-    public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) {
-        this(context, wifiEntry, new IconInjector(context));
-    }
-
-    @VisibleForTesting
-    WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry,
-            @NonNull IconInjector iconInjector) {
-        super(context);
-
-        setLayoutResource(R.layout.preference_access_point);
-        setWidgetLayoutResource(R.layout.access_point_friction_widget);
-        mFrictionSld = getFrictionStateListDrawable();
-        mWifiEntry = wifiEntry;
-        mWifiEntry.setListener(this);
-        mIconInjector = iconInjector;
-        refresh();
-    }
-
-    public WifiEntry getWifiEntry() {
-        return mWifiEntry;
-    }
-
-    @Override
-    public void onBindViewHolder(final PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-        final Drawable drawable = getIcon();
-        if (drawable != null) {
-            drawable.setLevel(mLevel);
-        }
-
-        view.itemView.setContentDescription(mContentDescription);
-
-        // Turn off divider
-        view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
-
-        // Enable the icon button when the help string in this WifiEntry is not null.
-        final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
-        final ImageView frictionImageView = (ImageView) view.findViewById(
-                R.id.friction_icon);
-        if (mWifiEntry.getHelpUriString() != null
-                && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
-            final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
-            drawablehelp.setTintList(
-                    Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal));
-            ((ImageView) imageButton).setImageDrawable(drawablehelp);
-            imageButton.setVisibility(View.VISIBLE);
-            imageButton.setOnClickListener(this);
-            imageButton.setContentDescription(
-                    getContext().getText(R.string.help_label));
-
-            if (frictionImageView != null) {
-                frictionImageView.setVisibility(View.GONE);
-            }
-        } else {
-            imageButton.setVisibility(View.GONE);
-
-            if (frictionImageView != null) {
-                frictionImageView.setVisibility(View.VISIBLE);
-                bindFrictionImage(frictionImageView);
-            }
-        }
-    }
-
-    /**
-     * Updates the title and summary; may indirectly call notifyChanged().
-     */
-    public void refresh() {
-        setTitle(mWifiEntry.getTitle());
-        final int level = mWifiEntry.getLevel();
-        final boolean showX = mWifiEntry.shouldShowXLevelIcon();
-        if (level != mLevel || showX != mShowX) {
-            mLevel = level;
-            mShowX = showX;
-            updateIcon(mShowX, mLevel);
-            notifyChanged();
-        }
-
-        setSummary(mWifiEntry.getSummary(false /* concise */));
-        mContentDescription = buildContentDescription();
-    }
-
-    /**
-     * Indicates the state of the WifiEntry has changed and clients may retrieve updates through
-     * the WifiEntry getter methods.
-     */
-    public void onUpdated() {
-        // TODO(b/70983952): Fill this method in
-        refresh();
-    }
-
-    /**
-     * Result of the connect request indicated by the WifiEntry.CONNECT_STATUS constants.
-     */
-    public void onConnectResult(int status) {
-        // TODO(b/70983952): Fill this method in
-    }
-
-    /**
-     * Result of the disconnect request indicated by the WifiEntry.DISCONNECT_STATUS constants.
-     */
-    public void onDisconnectResult(int status) {
-        // TODO(b/70983952): Fill this method in
-    }
-
-    /**
-     * Result of the forget request indicated by the WifiEntry.FORGET_STATUS constants.
-     */
-    public void onForgetResult(int status) {
-        // TODO(b/70983952): Fill this method in
-    }
-
-    /**
-     * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
-     */
-    public void onSignInResult(int status) {
-        // TODO(b/70983952): Fill this method in
-    }
-
-    protected int getIconColorAttr() {
-        final boolean accent = (mWifiEntry.hasInternetAccess()
-                && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
-        return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
-    }
-
-    private void updateIcon(boolean showX, int level) {
-        if (level == -1) {
-            setIcon(null);
-            return;
-        }
-
-        final Drawable drawable = mIconInjector.getIcon(showX, level);
-        if (drawable != null) {
-            drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), getIconColorAttr()));
-            setIcon(drawable);
-        } else {
-            setIcon(null);
-        }
-    }
-
-    @Nullable
-    private StateListDrawable getFrictionStateListDrawable() {
-        TypedArray frictionSld;
-        try {
-            frictionSld = getContext().getTheme().obtainStyledAttributes(FRICTION_ATTRS);
-        } catch (Resources.NotFoundException e) {
-            // Fallback for platforms that do not need friction icon resources.
-            frictionSld = null;
-        }
-        return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
-    }
-
-    /**
-     * Binds the friction icon drawable using a StateListDrawable.
-     *
-     * <p>Friction icons will be rebound when notifyChange() is called, and therefore
-     * do not need to be managed in refresh()</p>.
-     */
-    private void bindFrictionImage(ImageView frictionImageView) {
-        if (frictionImageView == null || mFrictionSld == null) {
-            return;
-        }
-        if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE)
-                && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) {
-            mFrictionSld.setState(STATE_SECURED);
-        }
-        frictionImageView.setImageDrawable(mFrictionSld.getCurrent());
-    }
-
-    /**
-     * Helper method to generate content description string.
-     */
-    @VisibleForTesting
-    CharSequence buildContentDescription() {
-        final Context context = getContext();
-
-        CharSequence contentDescription = getTitle();
-        final CharSequence summary = getSummary();
-        if (!TextUtils.isEmpty(summary)) {
-            contentDescription = TextUtils.concat(contentDescription, ",", summary);
-        }
-        int level = mWifiEntry.getLevel();
-        if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
-            contentDescription = TextUtils.concat(contentDescription, ",",
-                    context.getString(WIFI_CONNECTION_STRENGTH[level]));
-        }
-        return TextUtils.concat(contentDescription, ",",
-                mWifiEntry.getSecurity() == WifiEntry.SECURITY_NONE
-                        ? context.getString(R.string.accessibility_wifi_security_type_none)
-                        : context.getString(R.string.accessibility_wifi_security_type_secured));
-    }
-
-
-    static class IconInjector {
-        private final Context mContext;
-
-        IconInjector(Context context) {
-            mContext = context;
-        }
-
-        public Drawable getIcon(boolean showX, int level) {
-            return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
-        }
-    }
-
-    /**
-     * Set listeners, who want to listen the button client event.
-     */
-    public void setOnButtonClickListener(OnButtonClickListener listener) {
-        mOnButtonClickListener = listener;
-        notifyChanged();
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (view.getId() == R.id.icon_button) {
-            if (mOnButtonClickListener != null) {
-                mOnButtonClickListener.onButtonClick(this);
-            }
-        }
-    }
-
-    /**
-     * Callback to inform the caller that the icon button is clicked.
-     */
-    public interface OnButtonClickListener {
-
-        /**
-         * Register to listen the button click event.
-         */
-        void onButtonClick(WifiEntryPreference preference);
-    }
-
-    private Drawable getDrawable(@DrawableRes int iconResId) {
-        Drawable buttonIcon = null;
-
-        try {
-            buttonIcon = getContext().getDrawable(iconResId);
-        } catch (Resources.NotFoundException exception) {
-            // Do nothing
-        }
-        return buttonIcon;
-    }
-
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 6100615..56454e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -20,10 +20,13 @@
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason;
 
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiInfo;
+import android.os.Bundle;
 import android.os.SystemClock;
 
 import androidx.annotation.VisibleForTesting;
@@ -36,6 +39,23 @@
 
     private static final int INVALID_RSSI = -127;
 
+    /**
+     * The intent action shows network details settings to allow configuration of Wi-Fi.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: The calling package should put the chosen
+     * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+     * the {@link #KEY_CHOSEN_WIFIENTRY_KEY}.
+     * <p>
+     * Output: Nothing.
+     */
+    public static final String ACTION_WIFI_DETAILS_SETTINGS =
+            "android.settings.WIFI_DETAILS_SETTINGS";
+    public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
+    public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+
     static final int[] WIFI_PIE = {
             com.android.internal.R.drawable.ic_wifi_signal_0,
             com.android.internal.R.drawable.ic_wifi_signal_1,
@@ -275,7 +295,42 @@
         return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
     }
 
+    /**
+     * Wrapper the {@link #getInternetIconResource} for testing compatibility.
+     */
+    public static class InternetIconInjector {
+
+        protected final Context mContext;
+
+        public InternetIconInjector(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Returns the Internet icon for a given RSSI level.
+         *
+         * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+         * @param level The number of bars to show (0-4)
+         */
+        public Drawable getIcon(boolean noInternet, int level) {
+            return mContext.getDrawable(WifiUtils.getInternetIconResource(level, noInternet));
+        }
+    }
+
     public static boolean isMeteredOverridden(WifiConfiguration config) {
         return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
     }
+
+    /**
+     * Returns the Intent for Wi-Fi network details settings.
+     *
+     * @param key The Wi-Fi entry key
+     */
+    public static Intent getWifiDetailsSettingsIntent(String key) {
+        final Intent intent = new Intent(ACTION_WIFI_DETAILS_SETTINGS);
+        final Bundle bundle = new Bundle();
+        bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
+        return intent;
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java
new file mode 100644
index 0000000..4b34377
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.utils;
+
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class HandlerInjectorTest {
+
+    public static final long TEST_DELAY_MILLIS = 0L;
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock
+    public Handler mHandler;
+    @Mock
+    public Runnable mRunnable;
+
+    private HandlerInjector mHandlerInjector;
+
+    @Before
+    public void setUp() {
+        mHandlerInjector = new HandlerInjector(mHandler);
+    }
+
+    @Test
+    public void postDelayed_doByMainThreadHandler() {
+        mHandlerInjector.postDelayed(mRunnable, TEST_DELAY_MILLIS);
+
+        verify(mHandler).postDelayed(mRunnable, TEST_DELAY_MILLIS);
+    }
+
+    @Test
+    public void removeCallbacks_doByMainThreadHandler() {
+        mHandlerInjector.removeCallbacks(mRunnable);
+
+        verify(mHandler).removeCallbacks(mRunnable);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
deleted file mode 100644
index c21830b..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ /dev/null
@@ -1,254 +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.settingslib.wifi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.wifitrackerlib.WifiEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiEntryPreferenceTest {
-
-    private Context mContext;
-
-    @Mock
-    private WifiEntry mMockWifiEntry;
-    @Mock
-    private WifiEntryPreference.IconInjector mMockIconInjector;
-
-    @Mock
-    private Drawable mMockDrawable0;
-    @Mock
-    private Drawable mMockDrawable1;
-    @Mock
-    private Drawable mMockDrawable2;
-    @Mock
-    private Drawable mMockDrawable3;
-    @Mock
-    private Drawable mMockDrawable4;
-
-    @Mock
-    private Drawable mMockShowXDrawable0;
-    @Mock
-    private Drawable mMockShowXDrawable1;
-    @Mock
-    private Drawable mMockShowXDrawable2;
-    @Mock
-    private Drawable mMockShowXDrawable3;
-    @Mock
-    private Drawable mMockShowXDrawable4;
-
-    private static final String MOCK_TITLE = "title";
-    private static final String MOCK_SUMMARY = "summary";
-    private static final String FAKE_URI_STRING = "fakeuri";
-
-    @Before
-    public void setUp() {
-        mContext = RuntimeEnvironment.application;
-
-        MockitoAnnotations.initMocks(this);
-
-        when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
-        when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
-
-        when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0);
-        when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1);
-        when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2);
-        when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3);
-        when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4);
-
-        when(mMockIconInjector.getIcon(true /* showX */, 0))
-                .thenReturn(mMockShowXDrawable0);
-        when(mMockIconInjector.getIcon(true /* showX */, 1))
-                .thenReturn(mMockShowXDrawable1);
-        when(mMockIconInjector.getIcon(true /* showX */, 2))
-                .thenReturn(mMockShowXDrawable2);
-        when(mMockIconInjector.getIcon(true /* showX */, 3))
-                .thenReturn(mMockShowXDrawable3);
-        when(mMockIconInjector.getIcon(true /* showX */, 4))
-                .thenReturn(mMockShowXDrawable4);
-    }
-
-    @Test
-    public void constructor_shouldSetWifiEntryTitleAndSummary() {
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
-        assertThat(pref.getTitle()).isEqualTo(MOCK_TITLE);
-        assertThat(pref.getSummary()).isEqualTo(MOCK_SUMMARY);
-    }
-
-    @Test
-    public void constructor_shouldSetIcon() {
-        when(mMockWifiEntry.getLevel()).thenReturn(0);
-
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
-        assertThat(pref.getIcon()).isEqualTo(mMockDrawable0);
-    }
-
-    @Test
-    public void titleChanged_refresh_shouldUpdateTitle() {
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-        final String updatedTitle = "updated title";
-        when(mMockWifiEntry.getTitle()).thenReturn(updatedTitle);
-
-        pref.refresh();
-
-        assertThat(pref.getTitle()).isEqualTo(updatedTitle);
-    }
-
-    @Test
-    public void summaryChanged_refresh_shouldUpdateSummary() {
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-        final String updatedSummary = "updated summary";
-        when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
-
-        pref.refresh();
-
-        assertThat(pref.getSummary()).isEqualTo(updatedSummary);
-    }
-
-    @Test
-    public void levelChanged_refresh_shouldUpdateLevelIcon() {
-        final List<Drawable> iconList = new ArrayList<>();
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
-        when(mMockWifiEntry.getLevel()).thenReturn(0);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(1);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(2);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(3);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(4);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(-1);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-
-        assertThat(iconList).containsExactly(mMockDrawable0, mMockDrawable1,
-                mMockDrawable2, mMockDrawable3, mMockDrawable4, null);
-    }
-
-    @Test
-    public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() {
-        final List<Drawable> iconList = new ArrayList<>();
-        when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
-        when(mMockWifiEntry.getLevel()).thenReturn(0);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(1);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(2);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(3);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(4);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-        when(mMockWifiEntry.getLevel()).thenReturn(-1);
-        pref.refresh();
-        iconList.add(pref.getIcon());
-
-        assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
-                mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
-    }
-
-    @Test
-    public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
-        when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-        final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
-                false);
-        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
-        pref.onBindViewHolder(holder);
-
-        assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
-        when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-        final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
-                false);
-        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
-        pref.onBindViewHolder(holder);
-
-        assertThat(view.findViewById(R.id.icon_button).getContentDescription()).isEqualTo(
-                mContext.getString(R.string.help_label));
-    }
-
-    @Test
-    public void subscriptionEntry_shouldSetImageButtonGone() {
-        when(mMockWifiEntry.isSubscription()).thenReturn(true);
-        final WifiEntryPreference pref =
-                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-        final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
-                false);
-        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
-        pref.onBindViewHolder(holder);
-
-        assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.GONE);
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 89960cb..7c2b904 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,9 +20,12 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+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.Intent;
 import android.net.NetworkKey;
 import android.net.RssiCurve;
 import android.net.ScoredNetwork;
@@ -36,6 +39,8 @@
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 
+import androidx.test.core.app.ApplicationProvider;
+
 import com.android.settingslib.R;
 
 import org.junit.Before;
@@ -44,7 +49,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Set;
@@ -69,7 +73,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(ApplicationProvider.getApplicationContext());
     }
 
     @Test
@@ -148,6 +152,32 @@
         assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
     }
 
+    @Test
+    public void getWifiDetailsSettingsIntent_returnsCorrectValues() {
+        final String key = "test_key";
+
+        final Intent intent = WifiUtils.getWifiDetailsSettingsIntent(key);
+
+        assertThat(intent.getAction()).isEqualTo(WifiUtils.ACTION_WIFI_DETAILS_SETTINGS);
+        final Bundle bundle = intent.getBundleExtra(WifiUtils.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+        assertThat(bundle.getString(WifiUtils.KEY_CHOSEN_WIFIENTRY_KEY)).isEqualTo(key);
+    }
+
+    @Test
+    public void testInternetIconInjector_getIcon_returnsCorrectValues() {
+        WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
+
+        for (int level = 0; level <= 4; level++) {
+            iconInjector.getIcon(false /* noInternet */, level);
+            verify(mContext).getDrawable(
+                    WifiUtils.getInternetIconResource(level, false /* noInternet */));
+
+            iconInjector.getIcon(true /* noInternet */, level);
+            verify(mContext).getDrawable(
+                    WifiUtils.getInternetIconResource(level, true /* noInternet */));
+        }
+    }
+
     private static ArrayList<ScanResult> buildScanResultCache() {
         ArrayList<ScanResult> scanResults = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsProvider/res/values-mcc466/defaults.xml b/packages/SettingsProvider/res/values-mcc466/defaults.xml
new file mode 100644
index 0000000..fdeda88
--- /dev/null
+++ b/packages/SettingsProvider/res/values-mcc466/defaults.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Whether to enable mute when off body by default. -->
+    <bool name="def_wearable_muteWhenOffBodyEnabled">false</bool>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 8e6e251f..a9bc3be 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -214,6 +214,10 @@
     <!-- Default for Settings.System.VIBRATE_WHEN_RINGING -->
     <bool name="def_vibrate_when_ringing">false</bool>
 
+    <!-- Default for Settings.Global.CELL_ON; see PhoneConstants.CELL_ON_FLAG.
+        0: cellular off; 1: cellular on. -->
+    <integer name="def_cell_on">1</integer>
+
     <!-- Default for Settings.Global.APPLY_RAMPING_RINGER -->
     <bool name="def_apply_ramping_ringer">false</bool>
 
@@ -259,4 +263,64 @@
     <!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
     <bool name="def_one_handed_mode_activated">false</bool>
 
+    <!-- ========================================== -->
+    <!-- Default values for wear specific settings. -->
+
+    <bool name="def_wearable_hotwordDetectionEnabled">false</bool>
+
+    <bool name="def_wearable_smartIlluminateEnabled">true</bool>
+
+    <integer name="def_wearable_offChargerWifiUsageLimitMinutes">120</integer>
+
+    <!-- Default enabled state of accelerometer-based up/down gestures. -->
+    <bool name="def_wearable_upDownGesturesEnabled">false</bool>
+
+    <!-- Whether to enable mute when off body by default. -->
+    <bool name="def_wearable_muteWhenOffBodyEnabled">true</bool>
+
+    <!-- Whether to use an alternate launcher if available. -->
+    <bool name="def_wearable_alternateLauncherEnabled">true</bool>
+
+    <!-- If a square screen, how rounded the corners are. Same as CSS border-radius property. -->
+    <integer name="def_wearable_squareScreenCornerRoundness">0</integer>
+
+    <!-- Side button present -->
+    <bool name="def_wearable_sideButtonPresent">true</bool>
+
+    <!-- Android wear version. This value is a string due to no long type in resources -->
+    <string name="def_wearable_androidWearVersion" translatable="false">2</string>
+
+    <!-- This value is the decimal representation of the capabilities bitmask as defined below:
+        0000001 - WIFI
+        0000010 - Accounts
+        0000100 - Phone
+        0001000 - Cell
+        0010000 - Companion Legacy Calling
+        0100000 - Speaker
+        1000000 - Setup Protocomm Channel
+
+        Note: These must match the positions in
+            com.google.android.clockwork.common.system.WearSystemConstants  -->
+    <string name="def_wearable_systemCapabilities" translatable="false">3</string>
+
+    <!-- This value is used for the default system capabilities used on LE device. -->
+    <string name="def_wearable_leSystemCapabilities" translatable="false">1</string>
+
+    <!-- Brightness levels, on a 0-255 scale -->
+    <string name="def_wearable_brightnessLevels" translatable="false">255,204,153,102,51</string>
+
+    <!-- Whether to allow mobile signal detector by default. -->
+    <bool name="def_wearable_mobileSignalDetectorAllowed">true</bool>
+
+    <!-- If ambient mode is enabled by default. -->
+    <bool name="def_wearable_ambientEnabled">true</bool>
+
+    <!-- Whether tilt to wake is enabled by default. -->
+    <bool name="def_wearable_tiltToWakeEnabled">true</bool>
+
+    <!-- Whether touch to wake is enabled by default. -->
+    <bool name="def_wearable_touchToWakeEnabled">true</bool>
+
+    <!-- Whether tilt to bright is enabled by default. -->
+    <bool name="def_wearable_tiltToBrightEnabled">false</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index eb81961..b84a9cd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -40,6 +40,9 @@
     public static final String[] SETTINGS_TO_BACKUP = {
         Settings.Global.APPLY_RAMPING_RINGER,
         Settings.Global.BUGREPORT_IN_POWER_MENU,
+        Settings.Global.CLOCKWORK_SYSUI_PACKAGE_NAME,
+        Settings.Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME,
+        Settings.Global.CLOCKWORK_HOME_READY,
         Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
         Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
         Settings.Global.AUTO_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 5220a04..a9245f8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -113,6 +113,9 @@
         VALIDATORS.put(
                 Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
@@ -140,6 +143,155 @@
                         /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
         VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
+
+        VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.HOTWORD_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.SMART_REPLIES_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.RETAIL_MODE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.RETAIL_MODE_CONSUMER),
+                            String.valueOf(Global.Wearable.RETAIL_MODE_RETAIL)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.PLAY_STORE_AVAILABILITY,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.PLAY_STORE_AVAILABLE),
+                            String.valueOf(Global.Wearable.PLAY_STORE_UNAVAILABLE),
+                            String.valueOf(Global.Wearable.PLAY_STORE_AVAILABILITY_UNKNOWN)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.BUG_REPORT,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.BUG_REPORT_ENABLED),
+                            String.valueOf(Global.Wearable.BUG_REPORT_DISABLED)
+                        }));
+        VALIDATORS.put(Global.Wearable.SMART_ILLUMINATE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.CLOCKWORK_AUTO_TIME,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.SYNC_TIME_FROM_PHONE),
+                            String.valueOf(Global.Wearable.SYNC_TIME_FROM_NETWORK),
+                            String.valueOf(Global.Wearable.AUTO_TIME_OFF),
+                            String.valueOf(Global.Wearable.INVALID_AUTO_TIME_STATE)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE),
+                            String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_NETWORK),
+                            String.valueOf(Global.Wearable.AUTO_TIME_ZONE_OFF),
+                            String.valueOf(Global.Wearable.INVALID_AUTO_TIME_ZONE_STATE)
+                        }));
+        VALIDATORS.put(Global.Wearable.CLOCKWORK_24HR_TIME, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AUTO_WIFI, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.WIFI_POWER_SAVE, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+                ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.UPDOWN_GESTURES_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.SETUP_SKIPPED,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.SETUP_SKIPPED_YES),
+                            String.valueOf(Global.Wearable.SETUP_SKIPPED_NO),
+                            String.valueOf(Global.Wearable.SETUP_SKIPPED_UNKNOWN)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.LAST_CALL_FORWARD_ACTION,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_ON),
+                            String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_OFF),
+                            String.valueOf(Global.Wearable.CALL_FORWARD_NO_LAST_ACTION)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.STEM_1_TYPE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+                            String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+                            String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.STEM_2_TYPE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+                            String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+                            String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.STEM_3_TYPE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+                            String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+                            String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+                        }));
+        VALIDATORS.put(Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.ALTERNATE_LAUNCHER_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.CORNER_ROUNDNESS, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.SIDE_BUTTON, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.BUTTON_SET, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.ANDROID_WEAR_VERSION, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.WEAR_PLATFORM_MR_NUMBER, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.BOTTOM_OFFSET, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.DISPLAY_SHAPE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.DISPLAY_SHAPE_ROUND),
+                            String.valueOf(Global.Wearable.DISPLAY_SHAPE_SQUARE)
+                        }));
+        VALIDATORS.put(Global.Wearable.MOBILE_SIGNAL_DETECTOR, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_WAKE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_TOUCH_TO_WAKE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.DECOMPOSABLE_WATCHFACE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_GESTURE_SENSOR_ID, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_BRIGHT, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN),
+                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_ANDROID),
+                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.COMPANION_BLE_ROLE,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.BLUETOOTH_ROLE_CENTRAL),
+                            String.valueOf(Global.Wearable.BLUETOOTH_ROLE_PERIPHERAL)
+                        }));
+        VALIDATORS.put(
+                Global.Wearable.USER_HFP_CLIENT_SETTING,
+                new DiscreteValueValidator(
+                        new String[] {
+                            String.valueOf(Global.Wearable.HFP_CLIENT_UNSET),
+                            String.valueOf(Global.Wearable.HFP_CLIENT_ENABLED),
+                            String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
+                        }));
+        VALIDATORS.put(Global.Wearable.HFP_CLIENT_PROFILE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.ENABLE_ALL_LANGUAGES, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.OEM_SETUP_VERSION, ANY_INTEGER_VALIDATOR);
     }
 }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 268603f..cdf274f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2467,6 +2467,9 @@
             loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
                     R.bool.def_bluetooth_on);
 
+            loadIntegerSetting(stmt, Settings.Global.CELL_ON,
+                    R.integer.def_cell_on);
+
             // Enable or disable Cell Broadcast SMS
             loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
                     RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 00fd19c..f83ebaa 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -33,6 +33,7 @@
 import android.os.ShellCommand;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.provider.Settings.Config.SyncDisabledMode;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -69,7 +70,7 @@
             LIST,
             RESET,
             SET_SYNC_DISABLED_FOR_TESTS,
-            IS_SYNC_DISABLED_FOR_TESTS,
+            GET_SYNC_DISABLED_FOR_TESTS,
         }
 
         MyShellCommand(SettingsProvider provider) {
@@ -103,8 +104,8 @@
                 verb = CommandVerb.RESET;
             } else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
                 verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS;
-            } else if ("is_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
-                verb = CommandVerb.IS_SYNC_DISABLED_FOR_TESTS;
+            } else if ("get_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.GET_SYNC_DISABLED_FOR_TESTS;
                 if (peekNextArg() != null) {
                     perr.println("Bad arguments");
                     return -1;
@@ -117,7 +118,7 @@
             }
 
             // Parse args for those commands that have them.
-            int disableSyncMode = -1;
+            int syncDisabledModeArg = -1;
             int resetMode = -1;
             boolean makeDefault = false;
             String namespace = null;
@@ -154,15 +155,10 @@
                         }
                     }
                 } else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) {
-                    if (disableSyncMode == -1) {
-                        // DISABLE_SYNC_FOR_TESTS 1st arg (required)
-                        if ("none".equalsIgnoreCase(arg)) {
-                            disableSyncMode = SYNC_DISABLED_MODE_NONE;
-                        } else if ("persistent".equalsIgnoreCase(arg)) {
-                            disableSyncMode = SYNC_DISABLED_MODE_PERSISTENT;
-                        } else if ("until_reboot".equalsIgnoreCase(arg)) {
-                            disableSyncMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
-                        } else {
+                    if (syncDisabledModeArg == -1) {
+                        // SET_SYNC_DISABLED_FOR_TESTS 1st arg (required)
+                        syncDisabledModeArg = parseSyncDisabledMode(arg);
+                        if (syncDisabledModeArg == -1) {
                             // invalid
                             perr.println("Invalid sync disabled mode: " + arg);
                             return -1;
@@ -252,10 +248,16 @@
                     DeviceConfig.resetToDefaults(resetMode, namespace);
                     break;
                 case SET_SYNC_DISABLED_FOR_TESTS:
-                    DeviceConfig.setSyncDisabled(disableSyncMode);
+                    DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
                     break;
-                case IS_SYNC_DISABLED_FOR_TESTS:
-                    pout.println(DeviceConfig.isSyncDisabled());
+                case GET_SYNC_DISABLED_FOR_TESTS:
+                    int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+                    String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+                    if (syncDisabledModeString == null) {
+                        perr.println("Unknown mode: " + syncDisabledModeInt);
+                        return -1;
+                    }
+                    pout.println(syncDisabledModeString);
                     break;
                 default:
                     perr.println("Unspecified command");
@@ -295,8 +297,9 @@
                     + " syncing.");
             pw.println("        persistent: Sync is disabled, this state will survive a reboot.");
             pw.println("        until_reboot: Sync is disabled until the next reboot.");
-            pw.println("  is_sync_disabled_for_tests");
-            pw.println("      Prints 'true' if sync is disabled, 'false' otherwise.");
+            pw.println("  get_sync_disabled_for_tests");
+            pw.println("      Prints one of the SYNC_DISABLED_MODE values, see"
+                    + " set_sync_disabled_for_tests");
         }
 
         private boolean delete(IContentProvider provider, String namespace, String key) {
@@ -358,4 +361,31 @@
             }
         }
     }
+
+    private static @SyncDisabledMode int parseSyncDisabledMode(String arg) {
+        int syncDisabledMode;
+        if ("none".equalsIgnoreCase(arg)) {
+            syncDisabledMode = SYNC_DISABLED_MODE_NONE;
+        } else if ("persistent".equalsIgnoreCase(arg)) {
+            syncDisabledMode = SYNC_DISABLED_MODE_PERSISTENT;
+        } else if ("until_reboot".equalsIgnoreCase(arg)) {
+            syncDisabledMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
+        } else {
+            syncDisabledMode = -1;
+        }
+        return syncDisabledMode;
+    }
+
+    private static String formatSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
+        switch (syncDisabledMode) {
+            case SYNC_DISABLED_MODE_NONE:
+                return "none";
+            case SYNC_DISABLED_MODE_PERSISTENT:
+                return "persistent";
+            case SYNC_DISABLED_MODE_UNTIL_REBOOT:
+                return "until_reboot";
+            default:
+                return null;
+        }
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9cd7083..8c669d2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -848,9 +848,7 @@
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
                     mRestoredFromSdkInt);
 
-            if (DEBUG) {
-                Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
-            }
+            Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 073b4d0..579c3a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -763,9 +763,6 @@
                 Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
         dumpSetting(s, p,
-                Settings.Global.ANGLE_ALLOWLIST,
-                GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
-        dumpSetting(s, p,
                 Settings.Global.ANGLE_EGL_FEATURES,
                 GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e3ee22..9e7daf4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -43,6 +43,7 @@
 import android.app.compat.CompatChanges;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
+import android.bluetooth.BluetoothProfile;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
@@ -85,6 +86,7 @@
 import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -136,7 +138,6 @@
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 
-
 /**
  * <p>
  * This class is a content provider that publishes the system settings.
@@ -471,16 +472,16 @@
                 return result;
             }
 
-            case Settings.CALL_METHOD_SET_SYNC_DISABLED_CONFIG: {
+            case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG: {
                 final int mode = getSyncDisabledMode(args);
-                setSyncDisabledConfig(mode);
+                setSyncDisabledModeConfig(mode);
                 break;
             }
 
-            case Settings.CALL_METHOD_IS_SYNC_DISABLED_CONFIG: {
+            case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG: {
                 Bundle result = new Bundle();
-                result.putBoolean(Settings.KEY_CONFIG_IS_SYNC_DISABLED_RETURN,
-                        isSyncDisabledConfig());
+                result.putInt(Settings.KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN,
+                        getSyncDisabledModeConfig());
                 return result;
             }
 
@@ -1148,7 +1149,7 @@
         final String callingPackage = resolveCallingPackage();
 
         synchronized (mLock) {
-            if (isSyncDisabledConfigLocked()) {
+            if (getSyncDisabledModeConfigLocked() != SYNC_DISABLED_MODE_NONE) {
                 return SET_ALL_RESULT_DISABLED;
             }
             final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
@@ -1158,32 +1159,32 @@
         }
     }
 
-    private void setSyncDisabledConfig(@SyncDisabledMode int syncDisabledMode) {
+    private void setSyncDisabledModeConfig(@SyncDisabledMode int syncDisabledMode) {
         if (DEBUG) {
-            Slog.v(LOG_TAG, "setSyncDisabledConfig(" + syncDisabledMode + ")");
+            Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
         }
 
         enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
 
         synchronized (mLock) {
-            setSyncDisabledConfigLocked(syncDisabledMode);
+            setSyncDisabledModeConfigLocked(syncDisabledMode);
         }
     }
 
-    private boolean isSyncDisabledConfig() {
+    private int getSyncDisabledModeConfig() {
         if (DEBUG) {
-            Slog.v(LOG_TAG, "isSyncDisabledConfig");
+            Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
         }
 
         enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
 
         synchronized (mLock) {
-            return isSyncDisabledConfigLocked();
+            return getSyncDisabledModeConfigLocked();
         }
     }
 
     @GuardedBy("mLock")
-    private void setSyncDisabledConfigLocked(@SyncDisabledMode int syncDisabledMode) {
+    private void setSyncDisabledModeConfigLocked(@SyncDisabledMode int syncDisabledMode) {
         boolean persistentValue;
         boolean inMemoryValue;
         if (syncDisabledMode == SYNC_DISABLED_MODE_NONE) {
@@ -1215,13 +1216,13 @@
     }
 
     @GuardedBy("mLock")
-    private boolean isSyncDisabledConfigLocked() {
+    private int getSyncDisabledModeConfigLocked() {
         // Check the values used for both SYNC_DISABLED_MODE_PERSISTENT and
         // SYNC_DISABLED_MODE_UNTIL_REBOOT.
 
         // The SYNC_DISABLED_MODE_UNTIL_REBOOT value is cheap to check first.
         if (mSyncConfigDisabledUntilReboot) {
-            return true;
+            return SYNC_DISABLED_MODE_UNTIL_REBOOT;
         }
 
         // Now check the global setting used to implement SYNC_DISABLED_MODE_PERSISTENT.
@@ -1231,10 +1232,12 @@
                     SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
                     Global.DEVICE_CONFIG_SYNC_DISABLED);
             if (settingLocked == null) {
-                return false;
+                return SYNC_DISABLED_MODE_NONE;
             }
             String settingValue = settingLocked.getValue();
-            return settingValue != null && !"0".equals(settingValue);
+            boolean isSyncDisabledPersistent = settingValue != null && !"0".equals(settingValue);
+            return isSyncDisabledPersistent
+                    ? SYNC_DISABLED_MODE_PERSISTENT : SYNC_DISABLED_MODE_NONE;
         } finally {
             restoreCallingIdentity(callingIdentity);
         }
@@ -3585,7 +3588,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 203;
+            private static final int SETTINGS_VERSION = 204;
 
             private final int mUserId;
 
@@ -5189,6 +5192,199 @@
                     currentVersion = 203;
                 }
 
+                if (currentVersion == 203) {
+                        // Version 203: initialize entries migrated from wear settings provide.
+                        initGlobalSettingsDefaultValForWearLocked(Global.Wearable.HAS_PAY_TOKENS,
+                                false);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, 6);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.HOTWORD_DETECTION_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SMART_REPLIES_ENABLED, false);
+                        Setting locationMode = getSecureSettingsLocked(userId)
+                                                        .getSettingLocked(Secure.LOCATION_MODE);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+                                !locationMode.isNull()
+                                        && !Integer.toString(Secure.LOCATION_MODE_OFF)
+                                                .equals(locationMode.getValue()));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.RETAIL_MODE, Global.Wearable.RETAIL_MODE_CONSUMER);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.PLAY_STORE_AVAILABILITY,
+                                Global.Wearable.PLAY_STORE_AVAILABILITY_UNKNOWN);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.BUG_REPORT,
+                                "user".equals(Build.TYPE) // is user build?
+                                        ? Global.Wearable.BUG_REPORT_DISABLED
+                                        : Global.Wearable.BUG_REPORT_ENABLED);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SMART_ILLUMINATE_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_smartIlluminateEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.CLOCKWORK_AUTO_TIME,
+                                Global.Wearable.SYNC_TIME_FROM_PHONE);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+                                Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.CLOCKWORK_24HR_TIME, false);
+                        initGlobalSettingsDefaultValForWearLocked(Global.Wearable.AUTO_WIFI, true);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.WIFI_POWER_SAVE,
+                                getContext()
+                                        .getResources()
+                                        .getInteger(
+                                                R.integer
+                                                    .def_wearable_offChargerWifiUsageLimitMinutes));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS, 0L);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.UPDOWN_GESTURES_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_upDownGesturesEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SETUP_SKIPPED,
+                                Global.Wearable.SETUP_SKIPPED_UNKNOWN);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.LAST_CALL_FORWARD_ACTION,
+                                Global.Wearable.CALL_FORWARD_NO_LAST_ACTION);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_muteWhenOffBodyEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.WEAR_OS_VERSION_STRING, "");
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_alternateLauncherEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.CORNER_ROUNDNESS,
+                                getContext()
+                                        .getResources()
+                                        .getInteger(
+                                                R.integer
+                                                        .def_wearable_squareScreenCornerRoundness));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.BUTTON_SET, false);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SIDE_BUTTON,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_sideButtonPresent));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.ANDROID_WEAR_VERSION,
+                                Long.parseLong(
+                                        getContext()
+                                                .getResources()
+                                                .getString(
+                                                        R.string.def_wearable_androidWearVersion)));
+                        final int editionGlobal = 1;
+                        final int editionLocal = 2;
+                        boolean isLe =
+                                getContext().getPackageManager().hasSystemFeature("cn.google");
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SYSTEM_EDITION,
+                                isLe ? editionLocal : editionGlobal);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.SYSTEM_CAPABILITIES,
+                                getWearSystemCapabilities(isLe));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+                                SystemProperties.getInt("ro.cw_build.platform_mr", 0));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.BOTTOM_OFFSET, 0);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.DISPLAY_SHAPE,
+                                Settings.Global.Wearable.DISPLAY_SHAPE_SQUARE);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+                                getContext()
+                                        .getResources()
+                                        .getString(R.string.def_wearable_brightnessLevels));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(
+                                                R.bool.def_wearable_mobileSignalDetectorAllowed));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.AMBIENT_ENABLED,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_ambientEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.AMBIENT_TILT_TO_WAKE,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_tiltToWakeEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, false);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_touchToWakeEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+                                getContext()
+                                        .getResources()
+                                        .getBoolean(R.bool.def_wearable_tiltToBrightEnabled));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Global.Wearable.DECOMPOSABLE_WATCHFACE, false);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+                                SystemProperties.getBoolean("ro.ambient.force_when_docked", false));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+                                SystemProperties.getInt("ro.ambient.gesture_sensor_id", 0));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+                                SystemProperties.getBoolean("ro.ambient.low_bit_enabled", false));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+                                SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.COMPANION_ADDRESS, "");
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+                                Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+                                Settings.Global.Wearable.HFP_CLIENT_UNSET);
+                        Setting disabledProfileSetting =
+                                getGlobalSettingsLocked()
+                                        .getSettingLocked(
+                                                Settings.Global.BLUETOOTH_DISABLED_PROFILES);
+                        final long disabledProfileSettingValue =
+                                disabledProfileSetting.isNull()
+                                        ? 0
+                                        : Long.parseLong(disabledProfileSetting.getValue());
+                        final boolean isHfpClientProfileEnabled =
+                                (disabledProfileSettingValue
+                                                & (1 << BluetoothProfile.HEADSET_CLIENT))
+                                        == 0;
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+                                isHfpClientProfileEnabled);
+                        initGlobalSettingsDefaultValForWearLocked(
+                                Settings.Global.Wearable.COMPANION_OS_VERSION,
+                                Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
+
+                        // TODO(b/164398026): add necessary initialization logic for all entries.
+                        currentVersion = 204;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
@@ -5205,6 +5401,58 @@
                 // Return the current version.
                 return currentVersion;
             }
+
+            private void initGlobalSettingsDefaultValForWearLocked(String key, boolean val) {
+                initGlobalSettingsDefaultValForWearLocked(key, val ? "1" : "0");
+            }
+
+            private void initGlobalSettingsDefaultValForWearLocked(String key, int val) {
+                initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+            }
+
+            private void initGlobalSettingsDefaultValForWearLocked(String key, long val) {
+                initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+            }
+
+            private void initGlobalSettingsDefaultValForWearLocked(String key, String val) {
+                final SettingsState globalSettings = getGlobalSettingsLocked();
+                Setting currentSetting = globalSettings.getSettingLocked(key);
+                if (currentSetting.isNull()) {
+                    globalSettings.insertSettingOverrideableByRestoreLocked(
+                            key,
+                            val,
+                            null /* tag */,
+                            true /* makeDefault */,
+                            SettingsState.SYSTEM_PACKAGE_NAME);
+                }
+            }
+
+            private long getWearSystemCapabilities(boolean isLe) {
+                // Capability constants are imported from
+                // com.google.android.clockwork.common.system.WearableConstants.
+                final int capabilityCompanionLegacyCalling = 5;
+                final int capabilitySpeaker = 6;
+                final int capabilitySetupProtocommChannel = 7;
+                long capabilities =
+                        Long.parseLong(
+                                getContext().getResources()
+                                .getString(
+                                        isLe ? R.string.def_wearable_leSystemCapabilities
+                                                : R.string.def_wearable_systemCapabilities));
+                PackageManager pm = getContext().getPackageManager();
+                if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                    capabilities |= getBitMask(capabilityCompanionLegacyCalling);
+                }
+                if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+                    capabilities |= getBitMask(capabilitySpeaker);
+                }
+                capabilities |= getBitMask(capabilitySetupProtocommChannel);
+                return capabilities;
+            }
+
+            private long getBitMask(int capability) {
+                return 1 << (capability - 1);
+            }
         }
 
         /**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f04acd0..4aee164 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -47,6 +48,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
 import libcore.io.IoUtils;
@@ -806,7 +808,14 @@
 
                 final int settingCount = settings.size();
                 for (int i = 0; i < settingCount; i++) {
+
                     Setting setting = settings.valueAt(i);
+                    if (setting.isTransient()) {
+                        if (DEBUG_PERSISTENCE) {
+                            Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+                        }
+                        continue;
+                    }
 
                     if (writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
@@ -843,16 +852,20 @@
             } catch (Throwable t) {
                 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
                 if (t instanceof IOException) {
-                    // we failed to create a directory, so log the permissions and existence
-                    // state for the settings file and directory
-                    logSettingsDirectoryInformation(destination.getBaseFile());
+                    if (DEBUG) {
+                        // we failed to create a directory, so log the permissions and existence
+                        // state for the settings file and directory
+                        logSettingsDirectoryInformation(destination.getBaseFile());
+                    }
                     if (t.getMessage().contains("Couldn't create directory")) {
                         // attempt to create the directory with Files.createDirectories, which
                         // throws more informative errors than File.mkdirs.
                         Path parentPath = destination.getBaseFile().getParentFile().toPath();
                         try {
                             Files.createDirectories(parentPath);
-                            Slog.i(LOG_TAG, "Successfully created " + parentPath);
+                            if (DEBUG) {
+                                Slog.i(LOG_TAG, "Successfully created " + parentPath);
+                            }
                         } catch (Throwable t2) {
                             Slog.e(LOG_TAG, "Failed to write " + parentPath
                                     + " with Files.writeDirectories", t2);
@@ -1005,7 +1018,9 @@
             in = file.openRead();
         } catch (FileNotFoundException fnfe) {
             Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
-            logSettingsDirectoryInformation(mStatePersistFile);
+            if (DEBUG) {
+                logSettingsDirectoryInformation(mStatePersistFile);
+            }
             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
             return;
         }
@@ -1016,7 +1031,7 @@
         // Settings file exists but is corrupted. Retry with the fallback file
         final File statePersistFallbackFile = new File(
                 mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
-        Slog.i(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+        Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
                 + ", retrying with fallback file: " + statePersistFallbackFile);
         try {
             in = new AtomicFile(statePersistFallbackFile).openRead();
@@ -1302,6 +1317,14 @@
                     /* resetToDefault */ true);
         }
 
+        public boolean isTransient() {
+            switch (getTypeFromKey(getKey())) {
+                case SETTINGS_TYPE_GLOBAL:
+                    return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+            }
+            return false;
+        }
+
         public boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage, boolean overrideableByRestore) {
             return update(value, setDefault, packageName, tag, forceNonSystemPackage,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 3297937..f66d014 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -509,7 +509,6 @@
                     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,
@@ -593,7 +592,70 @@
                     Settings.Global.CACHED_APPS_FREEZER_ENABLED,
                     Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
                     Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
-                    Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
+                    Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+                    Settings.Global.Wearable.HAS_PAY_TOKENS,
+                    Settings.Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN,
+                    Settings.Global.Wearable.HOTWORD_DETECTION_ENABLED,
+                    Settings.Global.Wearable.SMART_REPLIES_ENABLED,
+                    Settings.Global.Wearable.DEFAULT_VIBRATION,
+                    Settings.Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+                    Settings.Global.Wearable.RETAIL_MODE,
+                    Settings.Global.Wearable.PLAY_STORE_AVAILABILITY,
+                    Settings.Global.Wearable.BUG_REPORT,
+                    Settings.Global.Wearable.SMART_ILLUMINATE_ENABLED,
+                    Settings.Global.Wearable.CLOCKWORK_AUTO_TIME,
+                    Settings.Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+                    Settings.Global.Wearable.CLOCKWORK_24HR_TIME,
+                    Settings.Global.Wearable.AUTO_WIFI,
+                    Settings.Global.Wearable.WIFI_POWER_SAVE,
+                    Settings.Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+                    Settings.Global.Wearable.UPDOWN_GESTURES_ENABLED,
+                    Settings.Global.Wearable.SETUP_SKIPPED,
+                    Settings.Global.Wearable.LAST_CALL_FORWARD_ACTION,
+                    Settings.Global.Wearable.STEM_1_TYPE,
+                    Settings.Global.Wearable.STEM_1_DATA,
+                    Settings.Global.Wearable.STEM_1_DEFAULT_DATA,
+                    Settings.Global.Wearable.STEM_2_TYPE,
+                    Settings.Global.Wearable.STEM_2_DATA,
+                    Settings.Global.Wearable.STEM_2_DEFAULT_DATA,
+                    Settings.Global.Wearable.STEM_3_TYPE,
+                    Settings.Global.Wearable.STEM_3_DATA,
+                    Settings.Global.Wearable.STEM_3_DEFAULT_DATA,
+                    Settings.Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+                    Settings.Global.Wearable.WEAR_OS_VERSION_STRING,
+                    Settings.Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+                    Settings.Global.Wearable.CORNER_ROUNDNESS,
+                    Settings.Global.Wearable.BUTTON_SET,
+                    Settings.Global.Wearable.SIDE_BUTTON,
+                    Settings.Global.Wearable.ANDROID_WEAR_VERSION,
+                    Settings.Global.Wearable.SYSTEM_CAPABILITIES,
+                    Settings.Global.Wearable.SYSTEM_EDITION,
+                    Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+                    Settings.Global.Wearable.COMPANION_BT_ADDRESS_DUAL,
+                    Settings.Global.Wearable.DISPLAY_SHAPE,
+                    Settings.Global.Wearable.BOTTOM_OFFSET,
+                    Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+                    Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+                    Settings.Global.Wearable.AMBIENT_ENABLED,
+                    Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
+                    Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV,
+                    Settings.Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+                    Settings.Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+                    Settings.Global.Wearable.DECOMPOSABLE_WATCHFACE,
+                    Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+                    Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+                    Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+                    Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+                    Settings.Global.Wearable.COMPANION_ADDRESS,
+                    Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+                    Settings.Global.Wearable.COMPANION_BLE_ROLE,
+                    Settings.Global.Wearable.COMPANION_NAME,
+                    Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+                    Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+                    Settings.Global.Wearable.COMPANION_OS_VERSION,
+                    Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
+                    Settings.Global.Wearable.SETUP_LOCALE,
+                    Settings.Global.Wearable.OEM_SETUP_VERSION);
 
     private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
@@ -763,8 +825,10 @@
 
     @Test
     public void globalSettingsBackedUpOrDenied() {
+        Set<String> candidateSettings = getCandidateSettings(Settings.Global.class);
+        candidateSettings.addAll(getCandidateSettings(Settings.Global.Wearable.class));
         checkSettingsBackedUpOrDenied(
-                getCandidateSettings(Settings.Global.class),
+                candidateSettings,
                 newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
                 BACKUP_DENY_LIST_GLOBAL_SETTINGS);
     }
@@ -792,8 +856,7 @@
                 .that(intersect(settingsToBackup, denylist)).isEmpty();
     }
 
-    private static Set<String> getCandidateSettings(
-            Class<? extends Settings.NameValueTable> clazz) {
+    private static Set<String> getCandidateSettings(Class<?> clazz) {
         HashSet<String> result = new HashSet<String>();
         for (Field field : clazz.getDeclaredFields()) {
             if (looksLikeValidSetting(field)) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1581e24..54fb647 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -571,6 +571,10 @@
     <!-- Permission required for GTS test - PendingSystemUpdateTest -->
     <uses-permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
 
+    <!-- Permission required to run the `vm` tool which manages on-device virtual machines -->
+    <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+    <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 504e18a..56b940c 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -256,6 +256,14 @@
         }
 
         setupAlert();
+
+        ListView listView = mAlert.getListView();
+        if (listView != null) {
+            // List view needs to gain focus in order for RSB to work.
+            if (!listView.requestFocus()) {
+                Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+            }
+        }
     }
     @Override
     public void onSaveInstanceState(Bundle outState) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d051290..228ee40 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -63,6 +63,7 @@
         "res",
     ],
     static_libs: [
+        "WifiTrackerLib",
         "WindowManager-Shell",
         "SystemUIAnimationLib",
         "SystemUIPluginLib",
@@ -144,6 +145,7 @@
         "src/**/I*.aidl",
     ],
     static_libs: [
+        "WifiTrackerLib",
         "SystemUIAnimationLib",
         "SystemUIPluginLib",
         "SystemUISharedLib",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 835471d..1cf14f2 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -1,5 +1,7 @@
 set noparent
 
+# Bug component: 78010
+
 dsandler@android.com
 
 aaliomer@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0d18b8d..24bffa1 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -53,8 +53,9 @@
 
   // Curious where your @Scenario tests will run?
   //
-  // @Ignore or @FlakyTest: nowhere
-  // @Staging: in staged-postsubmit, but not postsubmit or presubmit
+  // @Ignore: nowhere
+  // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
+  // 	presubmit
   // @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
   // none of the above: in presubmit, postsubmit, and staged-postsubmit
   //
@@ -98,9 +99,6 @@
         },
         {
             "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-            "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     }
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index c34012d..d816b3a 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -21,4 +21,8 @@
 
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">88dp</dimen>
+
+    <dimen name="qs_header_system_icons_area_height">0dp</dimen>
+    <dimen name="qs_panel_padding_top">0dp</dimen>
+
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values/donottranslate.xml b/packages/SystemUI/res-keyguard/values/donottranslate.xml
index a4d0ff7..1934457 100644
--- a/packages/SystemUI/res-keyguard/values/donottranslate.xml
+++ b/packages/SystemUI/res-keyguard/values/donottranslate.xml
@@ -21,6 +21,9 @@
     <!-- Skeleton string format for displaying the date when an alarm is set. -->
     <string name="abbrev_wday_month_day_no_year_alarm">EEEMMMd</string>
 
+    <!-- Skeleton string format for displaying the date shorter. -->
+    <string name="abbrev_month_day_no_year">MMMd</string>
+
     <!-- Skeleton string format for displaying the time in 12-hour format. -->
     <string name="clock_12hr_format">hm</string>
 
diff --git a/packages/SystemUI/res/drawable/ic_arrow_forward.xml b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
new file mode 100644
index 0000000..438e4c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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"
+        android:autoMirrored="true"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M6.23,20.23l1.77,1.77l10,-10l-10,-10l-1.77,1.77l8.23,8.23z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
new file mode 100644
index 0000000..2c34060
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:tint="?android:attr/colorControlNormal"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2H9V6zM18,20H6V10h12V20zM12,17c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2C10,16.1 10.9,17 12,17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
new file mode 100644
index 0000000..9a69b33
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="36dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="?android:attr/textColorPrimary"
+        android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..ac4c43b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+     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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
new file mode 100644
index 0000000..f38a368
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
+        android:strokeAlpha="0.3"
+        android:fillAlpha="0.3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,10h2v8h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,20h2v2h-2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_background.xml b/packages/SystemUI/res/drawable/internet_dialog_background.xml
new file mode 100644
index 0000000..3ceb0f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_background.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.
+  -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape android:shape="rectangle">
+        <corners android:radius="8dp" />
+        <solid android:color="?android:attr/colorBackground" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
new file mode 100644
index 0000000..50267fd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <stroke
+        android:color="?androidprv:attr/colorAccentPrimaryVariant"
+        android:width="1dp"/>
+    <corners android:radius="20dp"/>
+    <padding
+        android:left="8dp"
+        android:right="8dp"
+        android:top="4dp"
+        android:bottom="4dp" />
+    <solid android:color="@android:color/transparent" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
new file mode 100644
index 0000000..14672ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape android:shape="rectangle">
+        <corners
+            android:topLeftRadius="@dimen/internet_dialog_corner_radius"
+            android:topRightRadius="@dimen/internet_dialog_corner_radius"
+            android:bottomLeftRadius="@dimen/internet_dialog_corner_radius"
+            android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/>
+        <solid android:color="?android:attr/colorBackground" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
new file mode 100644
index 0000000..088e82b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/settingslib_state_off_color"/>
+            <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
new file mode 100644
index 0000000..250188b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/settingslib_state_on_color"/>
+            <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
new file mode 100644
index 0000000..b41762f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:left="@dimen/settingslib_switch_thumb_margin"
+        android:right="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid
+                android:color="@color/settingslib_thumb_off_color"
+                android:alpha="?android:attr/disabledAlpha"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_off.xml b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
new file mode 100644
index 0000000..8b69ad1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_off.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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:left="@dimen/settingslib_switch_thumb_margin"
+        android:right="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid android:color="@color/settingslib_thumb_off_color"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
new file mode 100644
index 0000000..0f27fc2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:left="@dimen/settingslib_switch_thumb_margin"
+        android:right="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid android:color="@color/settingslib_state_on_color"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
new file mode 100644
index 0000000..06bb779
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
+    <item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
+    <item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
new file mode 100644
index 0000000..15dfcb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <solid
+        android:color="@color/settingslib_track_off_color"
+        android:alpha="?android:attr/disabledAlpha"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_off_background.xml b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
new file mode 100644
index 0000000..4d79a6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
@@ -0,0 +1,24 @@
+<?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"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <solid android:color="@color/settingslib_track_off_color"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
new file mode 100644
index 0000000..c12d012
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -0,0 +1,24 @@
+<?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"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <solid android:color="@color/settingslib_track_on_color"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_selector.xml b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
new file mode 100644
index 0000000..a38c3b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
+    <item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
+    <item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml
deleted file mode 100644
index bc9c203..0000000
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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">
-    <TextView
-        android:id="@+id/global_actions_change_message"
-        android:layout_width="wrap_content"
-        android:visibility="gone"
-        android:layout_height="wrap_content"
-        android:text="@string/global_actions_change_description" />
-    <ImageView
-        android:id="@+id/global_actions_change_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
index 5588fd3..2430eec 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
@@ -13,12 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/global_actions_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:orientation="vertical"
     android:gravity="center"
     android:layout_gravity="center">
   <com.android.systemui.globalactions.GlobalActionsLayoutLite
@@ -28,11 +29,8 @@
       android:orientation="vertical"
       android:clipChildren="false"
       android:clipToPadding="false"
-      app:layout_constraintBottom_toBottomOf="parent"
-      app:layout_constraintTop_toTopOf="parent"
-      app:layout_constraintStart_toStartOf="parent"
-      app:layout_constraintEnd_toEndOf="parent"
-      android:layout_weight="1">
+      android:background="@drawable/global_actions_lite_background"
+      android:padding="@dimen/global_actions_lite_padding">
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -40,8 +38,6 @@
         android:gravity="center"
         android:translationZ="@dimen/global_actions_translate"
         android:orientation="horizontal"
-        android:background="@drawable/global_actions_lite_background"
-        android:padding="@dimen/global_actions_lite_padding"
         android:layoutDirection="ltr">
       <androidx.constraintlayout.helper.widget.Flow
           android:id="@+id/list_flow"
@@ -57,4 +53,4 @@
           app:flow_horizontalStyle="packed"/>
     </androidx.constraintlayout.widget.ConstraintLayout>
   </com.android.systemui.globalactions.GlobalActionsLayoutLite>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
new file mode 100644
index 0000000..7f22b71
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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/internet_connectivity_dialog"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/internet_dialog_rounded_top_corner_background"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        style="@style/Widget.SliceView.Panel"
+        android:gravity="center_vertical|center_horizontal"
+        android:layout_marginTop="24dp"
+        android:layout_marginBottom="24dp"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/internet_dialog_title"
+            android:gravity="center_vertical|center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="32dp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:fontFamily="google-sans"
+            android:textSize="24sp"/>
+
+        <TextView
+            android:id="@+id/internet_dialog_subtitle"
+            android:gravity="center_vertical|center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="20dp"
+            android:layout_marginTop="8dp"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:fontFamily="google-sans"
+            android:textSize="14sp"/>
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider"/>
+
+    <ProgressBar
+        android:id="@+id/wifi_searching_progress"
+        android:indeterminate="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="1dp"
+        android:maxHeight="1dp"
+        style="@*android:style/Widget.Material.ProgressBar.Horizontal"/>
+
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:id="@+id/scroll_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <LinearLayout
+                android:id="@+id/internet_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <LinearLayout
+                    android:id="@+id/mobile_network_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="88dp"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:layout_gravity="center_vertical|start"
+                    android:orientation="horizontal"
+                    android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+                    android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+                    android:layout_marginTop="4dp"
+                    android:layout_marginBottom="4dp"
+                    android:paddingStart="19dp"
+                    android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:clickable="false"
+                        android:layout_gravity="center_vertical|start">
+                        <ImageView
+                            android:id="@+id/signal_icon"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_weight="1"
+                        android:id="@+id/mobile_network_list"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:gravity="start|center_vertical">
+                        <TextView
+                            android:id="@+id/mobile_title"
+                            android:layout_marginLeft="17dp"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical"
+                            android:ellipsize="end"
+                            android:maxLines="1"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="16sp"
+                            android:fontFamily="google-sans"/>
+                        <TextView
+                            android:id="@+id/mobile_summary"
+                            android:layout_marginLeft="17dp"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical"
+                            android:ellipsize="end"
+                            android:maxLines="1"
+                            android:textColor="?android:attr/textColorTertiary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                    </LinearLayout>
+
+                    <FrameLayout
+                        android:layout_width="48dp"
+                        android:layout_height="48dp"
+                        android:layout_gravity="end|center_vertical">
+                        <Switch
+                            android:id="@+id/mobile_toggle"
+                            android:layout_gravity="center"
+                            android:layout_width="48dp"
+                            android:layout_height="wrap_content"
+                            android:track="@drawable/settingslib_track_selector"
+                            android:thumb="@drawable/settingslib_thumb_selector"
+                            android:theme="@style/MainSwitch.Settingslib"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <LinearLayout
+                    android:id="@+id/turn_on_wifi_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="48dp"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:gravity="center"
+                    android:orientation="horizontal"
+                    android:layout_marginTop="8dp"
+                    android:layout_marginBottom="8dp"
+                    android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+                    android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+                    android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+                    android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+                    <FrameLayout
+                        android:layout_weight="1"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent">
+                        <TextView
+                            android:text="@string/turn_on_wifi"
+                            android:layout_width="wrap_content"
+                            android:layout_height="match_parent"
+                            android:gravity="start|center_vertical"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="16sp"
+                            android:fontFamily="google-sans"/>
+                    </FrameLayout>
+
+                    <FrameLayout
+                        android:layout_width="48dp"
+                        android:layout_height="48dp"
+                        android:layout_marginTop="10dp"
+                        android:layout_marginBottom="10dp">
+                        <Switch
+                            android:id="@+id/wifi_toggle"
+                            android:layout_gravity="center"
+                            android:layout_width="48dp"
+                            android:layout_height="wrap_content"
+                            android:track="@drawable/settingslib_track_selector"
+                            android:thumb="@drawable/settingslib_thumb_selector"
+                            android:theme="@style/MainSwitch.Settingslib"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <LinearLayout
+                    android:id="@+id/wifi_connected_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="72dp"
+                    android:layout_gravity="center_vertical|start"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:visibility="gone"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:orientation="horizontal"
+                    android:layout_marginRight="@dimen/settingslib_switchbar_margin"
+                    android:layout_marginLeft="@dimen/settingslib_switchbar_margin"
+                    android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+                    android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:clickable="false"
+                        android:layout_gravity="center_vertical|start">
+                        <ImageView
+                            android:id="@+id/wifi_connected_icon"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_weight="3"
+                        android:id="@+id/wifi_connected_list"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="72dp"
+                        android:gravity="start|center_vertical">
+                        <TextView
+                            android:id="@+id/wifi_connected_title"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical"
+                            android:layout_marginLeft="16dp"
+                            android:ellipsize="end"
+                            android:maxLines="1"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                        <TextView
+                            android:id="@+id/wifi_connected_summary"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical"
+                            android:layout_marginLeft="16dp"
+                            android:ellipsize="end"
+                            android:maxLines="1"
+                            android:textColor="?android:attr/textColorTertiary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                    </LinearLayout>
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="match_parent"
+                        android:layout_marginRight="5dp"
+                        android:clickable="false"
+                        android:gravity="center">
+                        <ImageView
+                            android:id="@+id/wifi_settings_icon"
+                            android:src="@drawable/ic_settings_24dp"
+                            android:layout_width="24dp"
+                            android:layout_gravity="center"
+                            android:layout_height="wrap_content"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/wifi_list_layout"
+                    android:scrollbars="vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:overScrollMode="never"
+                    android:nestedScrollingEnabled="false"/>
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/see_all_layout"
+                android:layout_width="match_parent"
+                android:layout_height="64dp"
+                android:clickable="true"
+                android:focusable="true"
+                android:background="?android:attr/selectableItemBackground"
+                android:gravity="center_vertical|center_horizontal"
+                android:orientation="horizontal"
+                android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+                android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+                <FrameLayout
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:clickable="false"
+                    android:layout_gravity="center_vertical|start"
+                    android:layout_marginStart="16dp">
+                    <ImageView
+                        android:id="@+id/arrow_forward"
+                        android:src="@drawable/ic_arrow_forward"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"/>
+                </FrameLayout>
+
+                <FrameLayout
+                    android:orientation="vertical"
+                    android:clickable="false"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginLeft="16dp">
+                    <TextView
+                        android:text="@string/see_all_networks"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:gravity="start|center_vertical"
+                        android:textColor="?android:attr/textColorPrimary"
+                        android:textSize="14sp"
+                        android:fontFamily="google-sans"/>
+                </FrameLayout>
+
+            </LinearLayout>
+
+            <Space
+                android:id="@+id/space"
+                android:layout_width="match_parent"
+                android:layout_height="28dp"
+                android:visibility="gone"/>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="48dp"
+                android:layout_marginBottom="25dp"
+                android:gravity="end"
+                android:orientation="horizontal">
+                <Button
+                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                    android:id="@+id/done"
+                    android:layout_width="60dp"
+                    android:layout_height="30dp"
+                    android:layout_marginRight="24dp"
+                    android:layout_gravity="end"
+                    android:background="@drawable/internet_dialog_footer_background"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:text="@string/inline_done_button"
+                    android:textSize="14sp"
+                    android:fontFamily="google-sans"/>
+            </LinearLayout>
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
new file mode 100644
index 0000000..cb51ab6
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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/internet_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/wifi_list"
+        android:layout_width="match_parent"
+        android:layout_height="72dp"
+        android:layout_gravity="center_vertical|start"
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+        android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+        <FrameLayout
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:clickable="false"
+            android:layout_gravity="center_vertical|start"
+            android:layout_marginStart="16dp">
+            <ImageView
+                android:id="@+id/wifi_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"/>
+        </FrameLayout>
+
+        <LinearLayout
+            android:layout_weight="3"
+            android:id="@+id/wifi_network_layout"
+            android:orientation="vertical"
+            android:clickable="false"
+            android:layout_width="wrap_content"
+            android:layout_height="72dp">
+            <TextView
+                android:id="@+id/wifi_title"
+                android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_gravity="center_vertical"
+                android:gravity="start|center_vertical"
+                android:layout_marginLeft="16dp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"
+                android:fontFamily="google-sans"/>
+            <TextView
+                android:id="@+id/wifi_summary"
+                android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_gravity="center_vertical"
+                android:gravity="start|center_vertical"
+                android:layout_marginLeft="16dp"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="14sp"
+                android:fontFamily="google-sans"/>
+        </LinearLayout>
+
+        <FrameLayout
+            android:layout_width="24dp"
+            android:layout_height="match_parent"
+            android:layout_marginRight="@dimen/settingslib_switchbar_padding_right"
+            android:clickable="false"
+            android:gravity="center">
+            <ImageView
+                android:id="@+id/wifi_locked_icon"
+                android:layout_width="wrap_content"
+                android:layout_gravity="center"
+                android:layout_height="wrap_content"/>
+        </FrameLayout>
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 8ca1b8e..3be9993 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -19,7 +19,7 @@
     <View
         android:id="@+id/customizer_transparent_view"
         android:layout_width="match_parent"
-        android:layout_height="@*android:dimen/quick_qs_offset_height"
+        android:layout_height="@dimen/qs_header_system_icons_area_height"
         android:background="@android:color/transparent" />
 
     <com.android.keyguard.AlphaOptimizedLinearLayout
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 4c6418a..2ac03c2 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -13,13 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.systemui.qs.QSContainerImpl
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.qs.QSContainerImpl xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/quick_settings_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipToPadding="false"
-    android:clipChildren="false" >
+    android:clipChildren="false">
 
     <com.android.systemui.qs.NonInterceptingScrollView
         android:id="@+id/expanded_qs_scroll_view"
@@ -40,15 +40,33 @@
             android:accessibilityTraversalBefore="@android:id/edit"
             android:clipToPadding="false"
             android:clipChildren="false">
+
             <include layout="@layout/qs_footer_impl" />
         </com.android.systemui.qs.QSPanel>
     </com.android.systemui.qs.NonInterceptingScrollView>
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
-    <include android:id="@+id/qs_detail" layout="@layout/qs_detail" />
+    <include
+        android:id="@+id/qs_detail"
+        layout="@layout/qs_detail" />
 
-    <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
+    <include
+        android:id="@+id/qs_customize"
+        layout="@layout/qs_customize_panel"
         android:visibility="gone" />
 
+    <ImageView
+        android:id="@+id/qs_drag_handle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="24dp"
+        android:elevation="4dp"
+        android:importantForAccessibility="no"
+        android:scaleType="center"
+        android:src="@drawable/ic_qs_drag_handle"
+        android:tint="@color/qs_detail_button_white"
+        tools:ignore="UseAppTint" />
+
 </com.android.systemui.qs.QSContainerImpl>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 5b9ca1b..1086c2b 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,29 +21,50 @@
     android:layout_height="@*android:dimen/quick_qs_offset_height"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:minHeight="48dp"
+    android:minHeight="@dimen/qs_header_row_min_height"
     android:clickable="false"
     android:focusable="true"
     android:theme="@style/Theme.SystemUI.QuickSettings.Header">
 
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
+    <LinearLayout
+        android:id="@+id/clock_container"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:minWidth="48dp"
-        android:minHeight="48dp"
+        android:orientation="horizontal"
+        android:layout_gravity="center_vertical|start"
         android:gravity="center_vertical|start"
-        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.QS.Status" />
+        >
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:minWidth="@dimen/min_clickable_item_size"
+            android:minHeight="@dimen/min_clickable_item_size"
+            android:gravity="center_vertical|start"
+            android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.QS.Status" />
+
+        <com.android.systemui.statusbar.policy.VariableDateView
+            android:id="@+id/date_clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical|start"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.QS.Status"
+            systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+            systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+        />
+    </LinearLayout>
 
     <include layout="@layout/qs_carrier_group"
         android:id="@+id/carrier_group"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1"
-        android:minHeight="48dp"
+        android:minHeight="@dimen/qs_header_row_min_height"
         android:minWidth="48dp"
         android:layout_marginStart="8dp"
         android:layout_gravity="end|center_vertical"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index f9dcd39..df02730 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,7 +32,7 @@
     android:paddingStart="0dp"
     android:elevation="4dp" >
 
-    <!-- Date and privacy. Only visible in QS -->
+    <!-- Date and privacy. Only visible in QS when not in split shade -->
     <include layout="@layout/quick_status_bar_header_date_privacy"/>
 
     <RelativeLayout
@@ -42,21 +42,21 @@
         android:layout_gravity="top"
         android:clipChildren="false"
         android:clipToPadding="false">
-    <!-- Time, icons and Carrier (only in QS) -->
-    <include layout="@layout/quick_qs_status_icons"/>
+        <!-- Time, icons and Carrier (only in QS when not in split shade) -->
+        <include layout="@layout/quick_qs_status_icons"/>
 
-    <com.android.systemui.qs.QuickQSPanel
-        android:id="@+id/quick_qs_panel"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/quick_qs_status_icons"
-        android:layout_marginTop="@dimen/qqs_layout_margin_top"
-        android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:focusable="true"
-        android:paddingBottom="10dp"
-        android:importantForAccessibility="yes" />
+        <com.android.systemui.qs.QuickQSPanel
+            android:id="@+id/quick_qs_panel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/quick_qs_status_icons"
+            android:layout_marginTop="@dimen/qqs_layout_margin_top"
+            android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:focusable="true"
+            android:paddingBottom="24dp"
+            android:importantForAccessibility="yes" />
     </RelativeLayout>
 
 </com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index bff93a9..cc44b5e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -36,7 +36,7 @@
         android:layout_weight="1"
         android:gravity="center_vertical|start" >
 
-        <com.android.systemui.statusbar.policy.DateView
+        <com.android.systemui.statusbar.policy.VariableDateView
             android:id="@+id/date"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -44,7 +44,9 @@
             android:gravity="center_vertical"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.QS.Status"
-            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+            systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+            systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+        />
     </FrameLayout>
 
     <android.widget.Space
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 194d2e0..1c3eedb 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,16 +14,19 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<com.android.systemui.navigationbar.buttons.KeyButtonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/rotate_suggestion"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_weight="0"
-    android:scaleType="center"
-    android:visibility="invisible"
-    android:contentDescription="@string/accessibility_rotate_button"
-    android:paddingStart="@dimen/navigation_key_padding"
-    android:paddingEnd="@dimen/navigation_key_padding"
-/>
\ No newline at end of file
+    >
+
+    <com.android.systemui.navigationbar.buttons.KeyButtonView
+        android:id="@+id/rotate_suggestion"
+        android:layout_width="@dimen/floating_rotation_button_diameter"
+        android:layout_height="@dimen/floating_rotation_button_diameter"
+        android:contentDescription="@string/accessibility_rotate_button"
+        android:paddingStart="@dimen/navigation_key_padding"
+        android:paddingEnd="@dimen/navigation_key_padding"
+        android:layout_gravity="bottom|left"
+        android:scaleType="center"
+        android:visibility="invisible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
new file mode 100644
index 0000000..401dc19
--- /dev/null
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/split_shade_status_bar"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/split_shade_header_height"
+    android:minHeight="@dimen/split_shade_header_min_height"
+    android:clickable="false"
+    android:focusable="true"
+    android:paddingLeft="@dimen/qs_panel_padding"
+    android:paddingRight="@dimen/qs_panel_padding"
+    android:visibility="gone"
+    android:theme="@style/Theme.SystemUI.QuickSettings.Header">
+
+    <com.android.systemui.statusbar.policy.Clock
+        android:id="@+id/clock"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="48dp"
+        android:minHeight="@dimen/split_shade_header_min_height"
+        android:gravity="start|center_vertical"
+        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.QS.Status" />
+
+    <com.android.systemui.statusbar.policy.DateView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start|center_vertical"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.QS.Status"
+        systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+    <FrameLayout
+        android:id="@+id/rightLayout"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="end">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="end|center_vertical">
+
+            <include
+                android:id="@+id/carrier_group"
+                layout="@layout/qs_carrier_group"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_gravity="end|center_vertical"
+                android:layout_marginStart="8dp"
+                android:focusable="false"
+                android:minHeight="@dimen/split_shade_header_min_height"
+                android:minWidth="48dp" />
+
+            <com.android.systemui.statusbar.phone.StatusIconContainer
+                android:id="@+id/statusIcons"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+
+            <com.android.systemui.BatteryMeterView
+                android:id="@+id/batteryRemainingIcon"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                systemui:textAppearance="@style/TextAppearance.QS.Status" />
+        </LinearLayout>
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1a91202..cf91a2b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -67,6 +67,8 @@
         android:clipToPadding="false"
         android:clipChildren="false">
 
+        <include layout="@layout/split_shade_header"/>
+
         <include
             layout="@layout/keyguard_status_view"
             android:visibility="gone"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3506057..c5578f2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dit deblokkeer toegang vir alle programme en dienste wat toegelaat word om jou kamera te gebruik."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dit deblokkeer toegang vir alle programme en dienste wat toegelaat word om jou kamera of mikrofoon te gebruik."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Toestel"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swiep op om programme te wissel"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Sleep regs om programme vinnig te wissel"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Gelaai"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laai tans"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om oop te maak"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Stawing word vereis. Raak die vingerafdruksensor om te staaf."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Oproep aan die gang"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sal nie outomaties koppel nie"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding nie"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen ander netwerke beskikbaar nie"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerke beskikbaar nie"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkbesonderhede"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op \'n netwerk om te koppel"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 74009bf..4be2cee 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ይህ ካሜራዎን እንዲጠቀሙ ለተፈቀደላቸው ሁሉም መተግበሪያዎች እና አገልግሎቶች መዳረሻን እገዳ ያነሳል።"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ይህ የእርስዎን ካሜራ ወይም ማይክሮፎን እንዲጠቀሙ የተፈቀደላቸው የሁሉም መተግበሪያዎች እና አገልግሎቶች መዳረሻ እገዳን ያነሳል።"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"መሣሪያ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ኃይል በመሙላት ላይ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ለመክፈት የጣት አሻራ ይጠቀሙ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"የአውሮፕላን ሁነታ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"በይነመረብ በራስ-ሰር አይገናኝም"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ምንም አውታረ መረቦች የሉም"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"የአውታረ መረብ ዝርዝሮች"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ለመገናኘት አንድ አውታረ መረብ መታ ያድርጉ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 993ee39..300f3c6 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -441,6 +441,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"يؤدي هذا الخيار إلى إزالة حظر الوصول بالنسبة إلى كل التطبيقات والخدمات المسموح لها باستخدام الكاميرا."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"يؤدي هذا الخيار إلى إزالة حظر الوصول بالنسبة إلى كل التطبيقات والخدمات المسموح لها باستخدام الكاميرا أو الميكروفون."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"الجهاز"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"جارٍ الشحن"</string>
@@ -1182,4 +1184,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"يمكنك استخدام بصمة الإصبع للفتح"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"المصادقة مطلوبة. المس مستشعر بصمات الإصبع للمصادقة."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"مكالمة هاتفية جارية"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"وضع الطيران"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"لن يتم الاتصال بالإنترنت تلقائيًا."</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"لا يتوفّر اتصال بالإنترنت"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"لا تتوفّر شبكات أخرى."</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"لا تتوفّر أي شبكات."</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"تفاصيل الشبكة"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"انقر على إحدى الشبكات للاتصال بالإنترنت"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 20023c0..b06e010 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"এইটোৱে আপোনাৰ কেমেৰা ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়া আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে এক্সেছ অৱৰোধৰ পৰা আঁতৰায়।"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"এইটোৱে আপোনাৰ কেমেৰা অথবা মাইক্ৰ\'ফ\'ন ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়া আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে এক্সেছ অৱৰোধৰ পৰা আঁতৰায়।"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ডিভাইচ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"চ্চার্জ হৈ আছে"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলিবলৈ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ আৱশ্যক। বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিবলৈ ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"চলি থকা ফ’ন কল"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"এয়াৰপ্লেন ম\'ড"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইণ্টাৰনেট স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"ৱাই-ফাই"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"নেটৱৰ্কৰ সবিশেষ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"সংযোগ কৰিবলৈ এটা নেটৱৰ্কত টিপক"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 71f90fd5..4b9dfa5 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera və mikrofon istifadə edən bütün tətbiq və xidmətlərə giriş verir."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera və mikrofon istifadə edən bütün tətbiq və xidmətlərə giriş verir."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Tətbiqi dəyişmək üçün yuxarı sürüşdürün"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tətbiqləri cəld dəyişmək üçün sağa çəkin"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Enerji yığılıb"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Enerji doldurulur"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Doğrulanma tələb olunur. Doğrulamaq üçün barmaq izi sensoruna toxunun."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Davam edən zəng"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Uçuş rejimi"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Qoşulub"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternet avtomatik qoşulmayacaq"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yoxdur"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Heç bir başqa şəbəkə əlçatan deyil"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Əlçatan şəbəkə yoxdur"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Şəbəkə məlumatları"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Qoşulmaq üçün şəbəkəyə toxunun"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</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 7402594..3c29f85 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ovim će se odblokirati pristup za sve aplikacije i usluge koje imaju dozvolu za korišćenje kamere."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ovim će se odblokirati pristup za sve aplikacije i usluge koje imaju dozvolu za korišćenje kamere ili mikrofona."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Prevucite nagore da biste menjali aplikacije"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Prevucite udesno da biste brzo promenili aplikacije"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjena je"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Puni se"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je potvrda identiteta. Dodirnite senzor za otisak prsta da biste potvrdili identitet."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktuelni telefonski poziv"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Režim rada u avionu"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Automatsko povezivanje na internet nije moguće"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Veza nije uspostavljena"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7688e06..aefeb47 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Доступ адкрыецца для ўсіх праграм і сэрвісаў, якім дазволена выкарыстоўваць камеру."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Доступ адкрыецца для ўсіх праграм і сэрвісаў, якім дазволена выкарыстоўваць камеру ці мікрафон."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Прылада"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарадка"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Каб адкрыць, скарыстайце адбітак пальца"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Патрабуецца аўтэнтыфікацыя. Дакраніцеся да сканера адбіткаў пальцаў."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Бягучы тэлефонны выклік"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Рэжым палёту"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аўтаматычнае падключэнне да інтэрнэту адсутнічае"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма падключэння"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Больш няма даступных сетак"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Няма даступных сетак"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Інфармацыя пра сетку"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Каб падключыцца, націсніце на сетку"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 386fbd6..015504d 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Това действие отблокира достъпа за всички приложения и услуги, които имат разрешение да използват камерата ви."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Това действие отблокира достъпа за всички приложения и услуги, които имат разрешение да използват камерата или микрофона ви."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарежда се"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущо телефонно обаждане"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Самолетен режим"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Няма автоматично да се установи връзка с интернет"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма връзка"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Няма други налични мрежи"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Няма налични мрежи"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Подробности за мрежата"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Докоснете мрежа, за да се свържете"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index d269126..c8a4366 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"এটার জন্য ক্যামেরার অ্যাক্সেস সেই সব অ্যাপ এবং পরিষেবার জন্য আনব্লক হয়ে যাবে, যাতে আপনার ক্যামেরা ব্যবহারের অনুমতি দেওয়া হয়েছে।"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"এটার জন্য ক্যামেরা অথবা মাইক্রোফোনের অ্যাক্সেস সেই সব অ্যাপ এবং পরিষেবার জন্য আনব্লক হয়ে যাবে, যাতে আপনার ক্যামেরা অথবা মাইক্রোফোন ব্যবহারের অনুমতি দেওয়া হয়েছে।"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ডিভাইস"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"চার্জ হচ্ছে"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ব্যবহারকারী এখন ফোনে কথা বলছেন"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"বিমান মোড"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইন্টারনেট অটোমেটিক কানেক্ট হবে না"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"ওয়াই-ফাই"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"নেটওয়ার্কের বিবরণ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"কানেক্ট করতে একটি নেটওয়ার্কে ট্যাপ করুন"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 2857ecc..20fc2c6 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ovim se deblokira pristup za sve aplikacije i usluge kojima je dozvoljeno da koriste vašu kameru."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ovim se deblokira pristup za sve aplikacije i usluge kojima je dozvoljeno da koriste vašu kameru ili mikrofon."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Prevucite prema gore za promjenu aplikacije"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Prevucite udesno za brzu promjenu aplikacija"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Punjenje"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor za otisak prsta da autentificirate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u toku"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Način rada u avionu"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nije se moguće automatski povezati s internetom"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani s mrežom"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Druge mreže nisu dostupne"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da se povežete"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index c25dca5..ae12615 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Aquesta opció desbloqueja l\'accés de tots els serveis i aplicacions que tenen permís per utilitzar la càmera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Aquesta opció desbloqueja l\'accés de tots els serveis i aplicacions que tenen permís per utilitzar la càmera o el micròfon."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositiu"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Llisca cap amunt per canviar d\'aplicació"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrossega el dit cap a la dreta per canviar ràpidament d\'aplicació"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"S\'està carregant"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticació necessària. Toca el sensor d\'empremtes digitals per autenticar-te."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Trucada en curs"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Mode d\'avió"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no es connectarà automàticament"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sense connexió"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hi ha cap altra xarxa disponible"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No hi ha cap xarxa disponible"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalls de la xarxa"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una xarxa per connectar-te"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5fed1b3..b4937c6 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tímto odblokujete přístup všem aplikacím a službám, které mají povoleno používat váš fotoaparát."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tímto odblokujete přístup všem aplikacím a službám, které mají povoleno používat váš fotoaparát či mikrofon."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Zařízení"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Přejetím nahoru přepnete aplikace"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Přetažením doprava rychle přepnete aplikace"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Nabito"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nabíjení"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Je vyžadováno ověření. Dotkněte se snímače otisků prstů."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Probíhající hovor"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Režim Letadlo"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Připojeno"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet se nebude automaticky připojovat"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Žádné připojení"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Žádné další sítě nejsou k dispozici"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nejsou k dispozici žádné sítě"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti sítě"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Klepněte na síť, ke které se chcete připojit"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index facedf0f..9ee6edc 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dette fjerner adgangsblokeringen for alle apps og tjenester, der har tilladelse til at bruge dit kamera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dette fjerner adgangsblokeringen for alle apps og tjenester, der har tilladelse til at bruge dit kamera eller din mikrofon."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhed"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Stryg opad for at skifte apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Træk til højre for hurtigt at skifte app"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Opladet"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Oplader"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykslæseren for at godkende."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Igangværende telefonopkald"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Flytilstand"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Der oprettes ikke automatisk internetforbindelse"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Der er ingen forbindelse"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Der er ingen andre tilgængelige netværk"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Der er ingen tilgængelige netværk"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Netværksoplysninger"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryk på et netværk for at oprette forbindelse"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5d10eb4..5b31275 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dadurch wird die Blockierung des Zugriffs für alle Apps und Dienste aufgehoben, die deine Kamera verwenden dürfen."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dadurch wird die Blockierung des Zugriffs für alle Apps und Dienste aufgehoben, die deine Kamera oder dein Mikrofon verwenden dürfen."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Gerät"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Nach oben wischen, um Apps zu wechseln"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zum schnellen Wechseln der Apps nach rechts ziehen"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Aufgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Wird aufgeladen"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentifizierung erforderlich. Tippe dazu einfach auf den Fingerabdrucksensor."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktiver Anruf"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Flugmodus"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Keine automatische Verbindung mit dem Internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Keine Netzwerke verfügbar"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Netzwerkdetails"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tippe auf ein Netzwerk, um eine Verbindung herzustellen"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index cfda46f..1c1e806 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Με αυτόν τον τρόπο καταργείται ο αποκλεισμός της πρόσβασης για όλες τις εφαρμογές και υπηρεσίες που επιτρέπεται να χρησιμοποιούν την κάμερά σας."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Με αυτόν τον τρόπο καταργείται ο αποκλεισμός της πρόσβασης για όλες τις εφαρμογές και υπηρεσίες που επιτρέπεται να χρησιμοποιούν την κάμερα ή το μικρόφωνό σας."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Συσκευή"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Φόρτιση"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Τηλεφωνική κλήση σε εξέλιξη"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Λειτουργία πτήσης"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Χωρίς αυτόματη σύνδεση στο διαδίκτυο"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Χωρίς σύνδεση"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Δεν υπάρχουν άλλα διαθέσιμα δίκτυα"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Λεπτομέρειες δικτύου"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Πατήστε ένα δίκτυο για να συνδεθείτε"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9af2d89..38630e1 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe up to switch apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Drag right to quickly switch apps"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 398420a..0669461 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe up to switch apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Drag right to quickly switch apps"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9af2d89..38630e1 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe up to switch apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Drag right to quickly switch apps"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9af2d89..38630e1 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe up to switch apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Drag right to quickly switch apps"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index b211026..5668317 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎This unblocks access for all apps and services allowed to use your camera.‎‏‎‎‏‎"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎This unblocks access for all apps and services allowed to use your camera or microphone.‎‏‎‎‏‎"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎Device‎‏‎‎‏‎"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎Swipe up to switch apps‎‏‎‎‏‎"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎Drag right to quickly switch apps‎‏‎‎‏‎"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎Toggle Overview‎‏‎‎‏‎"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎Charged‎‏‎‎‏‎"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎Charging‎‏‎‎‏‎"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎Use fingerprint to open‎‏‎‎‏‎"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎Authentication required. Touch the fingerprint sensor to authenticate.‎‏‎‎‏‎"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎Ongoing phone call‎‏‎‎‏‎"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎Airplane mode‎‏‎‎‏‎"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎Mobile data‎‏‎‎‏‎"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="STATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="NETWORKMODE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎Connected‎‏‎‎‏‎"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎Internet won\'t auto‑connect‎‏‎‎‏‎"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎No connection‎‏‎‎‏‎"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎No other networks available‎‏‎‎‏‎"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎No networks available‎‏‎‎‏‎"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎Wi‑Fi‎‏‎‎‏‎"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎Network details‎‏‎‎‏‎"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎Tap a network to connect‎‏‎‎‏‎"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎Searching for networks…‎‏‎‎‏‎"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎Failed to connect to network‎‏‎‎‏‎"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎See all‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 55b0f98..c04d600 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Esta acción desbloquea el acceso para todos los servicios y las apps que tengan permitido usar la cámara."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Esta acción permite que todas las aplicaciones y servicios que tengan permiso puedan usar la cámara o el micrófono."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Desliza el dedo hacia arriba para cambiar de app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastra a la derecha para cambiar aplicaciones rápidamente"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo de avión"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conexión establecida"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"No se conectará automáticamente a Internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Presiona una red para conectarte a ella"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c5096e9..bbaf208 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Si lo haces, todas las aplicaciones y servicios que tengan permiso podrán usar tu cámara."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Si lo haces, todas las aplicaciones y servicios que tengan permiso podrán usar tu cámara o tu micrófono."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Desliza el dedo hacia arriba para cambiar de aplicación"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastra hacia la derecha para cambiar rápidamente de aplicación"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticación obligatoria. Toca el sensor de huellas digitales para autenticarte."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no se conecta automáticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una red para conectarte"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ee7c760..b23b283 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Sellega tühistatakse juurdepääsu blokeerimine kõikide rakenduste ja teenuste puhul, millel on lubatud kaamerat kasutada."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Sellega tühistatakse juurdepääsu blokeerimine kõikide rakenduste ja teenuste puhul, millel on lubatud kaamerat või mikrofoni kasutada."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Seade"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rakenduste vahetamiseks pühkige üles"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lohistage paremale, et rakendusi kiiresti vahetada"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Laetud"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laadimine"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Kasutage avamiseks sõrmejälge"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vajalik on autentimine. Puudutage autentimiseks sõrmejäljeandurit."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Käimasolev telefonikõne"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Lennukirežiim"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetiühendust ei looda automaatselt"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ühendus puudub"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ühtegi muud võrku pole saadaval"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Ühtegi võrku pole saadaval"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Võrgu üksikasjad"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Puudutage ühendamiseks võrku"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0972c26b..6344f62 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera edo mikrofonoa atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dituzte."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Gailua"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Egin gora aplikazioa aldatzeko"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastatu eskuinera aplikazioa azkar aldatzeko"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Kargatuta"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Kargatzen"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentifikazioa behar da. Autentifikatzeko, ukitu hatz-marken sentsorea."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefono-dei bat abian da"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Hegaldi modua"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> (<xliff:g id="NETWORKMODE">%2$s</xliff:g>)"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Ez da automatikoki konektatuko Internetera"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Konexiorik gabe"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ez dago beste sare erabilgarririk"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Ez dago sare erabilgarririk"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wifia"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Sarearen xehetasunak"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Sakatu sare bat hartara konektatzeko"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 2899e17..6988df2 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"با این کار دسترسی برای همه برنامه‌ها و سرویس‌هایی که مجاز هستند از دوربینتان استفاده کنند لغو انسداد می‌شود."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"با این کار دسترسی برای همه برنامه‌ها و دستگاه‌هایی که مجاز هستند از دوربین یا میکروفونتان استفاده کنند لغو انسداد می‌شود."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"دستگاه"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"در حال شارژ شدن"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالت‌سنجی لازم است. برای اصالت‌سنجی، حسگر اثر انگشت را لمس کنید."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"حالت هواپیما"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"اینترنت به‌طور خودکار متصل نخواهد شد"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"اتصال برقرار نیست"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"شبکه‌ای در دسترس نیست"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"جزئیات شبکه"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"برای اتصال به شبکه روی آن ضربه بزنید"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index dc49957..29304c89 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tämä kumoaa kaikkien sellaisten sovellusten ja palveluiden eston, joilla on lupa käyttää kameraasi."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tämä kumoaa eston kaikkien sellaisten sovellusten ja palveluiden osalta, joilla on lupa käyttää kameraasi tai mikrofoniasi."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Laite"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vaihda sovellusta pyyhkäisemällä ylös"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Vaihda sovellusta nopeasti vetämällä oikealle"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Ladattu"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Ladataan"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Todennus vaaditaan. Todenna koskettamalla sormenjälkitunnistinta."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Puhelu käynnissä"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Lentokonetila"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetyhteyttä ei muodosteta automaattisesti"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ei yhteyttä"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ei muita verkkoja käytettävissä"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Ei verkkoja käytettävissä"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Verkon tiedot"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Muodosta yhteys napauttamalla verkkoa"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 83c0ef4..a479e83 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Cette action débloque l\'accès pour toutes les applications et pour tous les services autorisés à utiliser l\'appareil photo."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Cette action débloque l\'accès pour toutes les applications et tous les services autorisés à utiliser l\'appareil photo ou le microphone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Appareil"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Balayez vers le haut pour changer d\'application"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Balayez l\'écran vers la droite pour changer rapidement d\'application"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Chargée"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charge en cours..."</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Touchez le capteur d\'empreintes digitales pour vous authentifier."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours…"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connexion active"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Connexion automatique à Internet impossible"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau n\'est accessible"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Touchez un réseau pour vous y connecter"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index bb3183e..fef652b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Cette action débloque l\'accès à tous les services et applis autorisés à utiliser votre appareil photo."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Cette action débloque l\'accès pour tous les services et applis autorisés à utiliser votre appareil photo ou votre micro."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Appareil"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Balayer l\'écran vers le haut pour changer d\'application"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Déplacer vers la droite pour changer rapidement d\'application"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'aperçu"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Chargé"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"En charge"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Appuyez sur le lecteur d\'empreintes digitales pour vous authentifier."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Pas de connexion automatique à Internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau disponible"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau disponible"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Appuyez sur un réseau pour vous connecter"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d8fe4b8..9e91ae5 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Con esta acción desbloquearase o acceso á cámara para todas as aplicacións e servizos que teñan permiso para utilizala."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Con esta acción desbloquearase o acceso á cámara ou ao micrófono para todas as aplicacións e servizos que teñan permiso para utilizalos."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Pasar o dedo cara arriba para cambiar de aplicación"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastra cara á dereita para cambiar de aplicacións rapidamente"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargado"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Requírese autenticación. Para autenticarte, toca o sensor de impresión dixital."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica en curso"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non se conectará automaticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sen conexión"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Non hai outras redes dispoñibles"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Non hai redes dispoñibles"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles da rede"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca unha rede para conectarte a ela"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 408e798..e099447 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"આ તમારા કૅમેરાનો ઉપયોગ કરવાની મંજૂરી ધરાવતી તમામ ઍપ અને સેવાઓ માટે ઍક્સેસને અનબ્લૉક કરે છે."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"આ તમારા કૅમેરા અથવા માઇક્રોફોનનો ઉપયોગ કરવાની મંજૂરી ધરાવતી તમામ ઍપ અને સેવાઓ માટે ઍક્સેસને અનબ્લૉક કરે છે."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ડિવાઇસ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ચાર્જ થઈ રહ્યું છે"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ફોન કૉલ ચાલુ છે"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"એરપ્લેન મોડ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ઇન્ટરનેટ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"વાઇ-ફાઇ"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"નેટવર્કની વિગતો"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"કનેક્ટ કરવા માટે નેટવર્ક પર ટૅપ કરો"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index ac4a903..fde2a39 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ऐसा करने से, कैमरे का ऐक्सेस उन सभी ऐप्लिकेशन और सेवाओं के लिए अनब्लॉक हो जाएगा जिन्हें कैमरे का इस्तेमाल करने की अनुमति दी गई है."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ऐसा करने से, कैमरा या माइक्रोफ़ोन का ऐक्सेस उन सभी ऐप्लिकेशन और सेवाओं के लिए अनब्लॉक हो जाएगा जिन्हें ये इस्तेमाल करने की अनुमति है."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"डिवाइस"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हो रही है"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"खोलने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि करना ज़रूरी है. पुष्टि करने के लिए, फ़िंगरप्रिंट सेंसर को छुएं."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फ़ोन कॉल चल रहा है"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"हवाई जहाज़ मोड"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट अपने-आप कनेक्ट नहीं होगा"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"इंटरनेट कनेक्शन नहीं है"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"कोई दूसरा नेटवर्क उपलब्ध नहीं है"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"कोई नेटवर्क उपलब्ध नहीं है"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"वाई-फ़ाई"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्क की जानकारी"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इंटरनेट से कनेक्ट करने के लिए, किसी नेटवर्क पर टैप करें"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 571ca24..bbba1a8 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Time se deblokira pristup za sve aplikacije i usluge kojima je dopuštena upotreba vašeg fotoaparata."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Time se deblokira pristup za sve aplikacije i usluge kojima je dopuštena upotreba vašeg fotoaparata ili mikrofona."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Prijeđite prstom prema gore da biste promijenili aplikaciju"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Povucite udesno da biste brzo promijenili aplikaciju"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Punjenje"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor otiska prsta da biste se autentificirali."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u tijeku"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Način rada u zrakoplovu"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Neće biti automatskog povezivanja s internetom"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Pojedinosti o mreži"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 9a7ca9d6..7eb5ec1 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ezzel feloldja a hozzáférés letiltását az összes olyan alkalmazás és szolgáltatás esetében, amelyek számára engedélyezte a kamera használatát."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ezzel feloldja a hozzáférés letiltását az összes olyan alkalmazás és szolgáltatás esetében, amelyek számára engedélyezte a kamera vagy a mikrofon használatát."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Váltás az alkalmazások között felfelé csúsztatással"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Húzza jobbra az ujját az alkalmazások közötti gyors váltáshoz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Feltöltve"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Töltés"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ujjlenyomat használata a megnyitáshoz"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Hitelesítés szükséges. Érintse meg az ujjlenyomat-érzékelőt a hitelesítéshez."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Folyamatban lévő telefonhívás"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Repülős üzemmód"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g>/<xliff:g id="STATE">%1$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Az internetre történő csatlakozás nem automatikus"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nincs kapcsolat"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nincs több rendelkezésre álló hálózat"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nincs rendelkezésre álló hálózat"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Hálózati információk"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"A kapcsolódáshoz koppintson a kívánt hálózatra"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 74ab34d..95b2d6a 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Սա բացում է մուտքը բոլոր հավելվածների և ծառայությունների համար, որոնք ունեն ձեր տեսախցիկն օգտագործելու թույլտվություն։"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Սա բացում է մուտքը բոլոր հավելվածների և ծառայությունների համար, որոնք ունեն ձեր տեսախցիկը կամ խոսափողն օգտագործելու թույլտվություն։"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Սարք"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Լիցքավորվում է"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Բացելու համար օգտագործեք մատնահետքը"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Պահանջվում է նույնականացում։ Դրա համար մատը հպեք մատնահետքի սկաներին։"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ընթացիկ հեռախոսազանգ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Ավիառեժիմ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Չհաջողվեց ավտոմատ միանալ համացանցին"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Կապ չկա"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Այլ հասանելի ցանցեր չկան"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Հասանելի ցանցեր չկան"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Ցանցի տվյալներ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Հպեք ցանցին՝ միանալու համար"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 204484c..193628a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Langkah ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera atau mikrofon."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Perangkat"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Geser ke atas untuk beralih aplikasi"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tarik ke kanan untuk beralih aplikasi dengan cepat"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Terisi penuh"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Mengisi daya"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan sidik jari untuk membuka"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Perlu autentikasi. Sentuh sensor sidik jari untuk melakukan autentikasi."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telepon sedang berlangsung"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Mode pesawat"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan terhubung otomatis"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tidak ada koneksi"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Jaringan lain tidak tersedia"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Jaringan tidak tersedia"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detail jaringan"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketuk jaringan untuk menghubungkan"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 5afc8bc..dbc7c05 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Þetta veitir öllum forritum og þjónustum aðgang að myndavélinni þinni."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Þetta veitir öllum forritum og þjónustum aðgang að myndavélinni og hljóðnemanum þínum."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Tæki"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Strjúktu upp til að skipta á milli forrita"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Dragðu til hægri til að skipta hratt á milli forrita"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Fullhlaðin"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Í hleðslu"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Auðkenningar krafist. Auðkenndu með því að snerta fingrafaralesarann."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Símtal í gangi"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Flugstilling"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetið tengist ekki sjálfkrafa"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Engin tenging"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Engin önnur net í boði"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Ekkert net í boði"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Upplýsingar um net"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ýttu á net til að tengjast"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9c28363..6f160b9 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Viene sbloccato l\'accesso per tutti i servizi e le app autorizzati a usare la fotocamera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Viene sbloccato l\'accesso per tutti i servizi e le app autorizzati a usare la fotocamera o il microfono."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Scorri verso l\'alto per passare ad altre app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Trascina verso destra per cambiare velocemente app"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carica"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"In carica"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa l\'impronta per aprire"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticazione obbligatoria. Eseguila toccando il sensore di impronte digitali."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonata in corso"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modalità aereo"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Connessione attiva"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non si connetterà automaticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nessuna connessione"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nessun\'altra rete disponibile"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nessuna rete disponibile"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Dettagli rete"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tocca una rete per connetterti"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index b7153b1..9dccc98 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"הפעולה הזו מבטלת את חסימת הגישה של כל האפליקציות והשירותים שמורשים להשתמש במצלמה."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"הפעולה הזו מבטלת את חסימת הגישה של כל האפליקציות והשירותים שמורשים להשתמש במצלמה או במיקרופון."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"מכשיר"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"בטעינה"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"מצב טיסה"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"לא ניתן להתחבר לאינטרנט באופן אוטומטי"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"אין חיבור"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"אין רשתות זמינות אחרות"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"אין רשתות זמינות"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"פרטי הרשת"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"צריך להקיש על רשת כדי להתחבר"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a8fb87d..1e8020f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"カメラの使用が許可されているすべてのアプリとサービスでアクセスのブロックが解除されます。"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"カメラやマイクの使用が許可されているすべてのアプリとサービスでアクセスのブロックが解除されます。"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"デバイス"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電しています"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"指紋を使って開いてください"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"認証が必要です。指紋認証センサーをタッチして認証してください。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"機内モード"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"インターネットに自動的に接続されません"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"接続なし"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"利用できるネットワークはありません"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ネットワークを利用できません"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ネットワークの詳細"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ネットワークをタップして接続"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index da4f5bc..8177464 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ამ მოქმედების მეშვეობით განიბლოკება ყველა აპსა და მომსახურებაზე წვდომა, რომელთაც აქვთ თქვენი კამერის გამოყენების უფლება."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ამ მოქმედების მეშვეობით განიბლოკება ყველა აპსა და მომსახურებაზე წვდომა, რომელთაც აქვთ თქვენი კამერის ან მიკროფონის გამოყენების უფლება."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"მოწყობილობა"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"მიმდინარეობს დატენვა"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"გასახსნელად გამოიყენეთ თითის ანაბეჭდი"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"თვითმფრინავის რეჟიმი"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ინტერნეტს ავტომატურად არ დაუკავშირდება"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"კავშირი არ არის"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"სხვა ქსელები მიუწვდომელია"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ქსელები მიუწვდომელია"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ქსელის დეტალები"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"დასაკავშირებლად შეეხეთ ქსელს"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index c15c6be..983e4b8 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Камераңызды пайдалануға рұқсат берілген барлық қолданба мен қызметтің бөгеуі алынады."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Камераңызды немесе микрофоныңызды пайдалануға рұқсат берілген барлық қолданба мен қызметтің бөгеуі алынады."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Құрылғы"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарядталуда"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Телефон қоңырауы бар"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Ұшақ режимі"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматты түрде қосылмайды."</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыс жоқ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Басқа қолжетімді желі жоқ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Қолжетімді желілер жоқ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Желі деректері"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Желіге қосылу үшін оны түртіңіз."</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 31de2be..183712b 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ការធ្វើបែបនេះ​នឹងឈប់ទប់ស្កាត់​ការចូលប្រើ​សម្រាប់កម្មវិធី និងសេវាកម្ម​ទាំងអស់ ដែលត្រូវបាន​អនុញ្ញាតឱ្យប្រើ​កាមេរ៉ារបស់អ្នក​។"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ការធ្វើបែបនេះ​នឹងឈប់ទប់ស្កាត់​ការចូលប្រើ​សម្រាប់កម្មវិធី និងសេវាកម្ម​ទាំងអស់ ដែលត្រូវបាន​អនុញ្ញាតឱ្យប្រើ​កាមេរ៉ា ឬមីក្រូហ្វូនរបស់អ្នក​។"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ឧបករណ៍"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"កំពុងសាក​ថ្ម"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ប្រើស្នាមម្រាមដៃ ដើម្បីបើក"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"តម្រូវឱ្យ​មាន​ការផ្ទៀងផ្ទាត់។ សូមចុច​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ ដើម្បី​ផ្ទៀងផ្ទាត់​។"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ការហៅទូរសព្ទ​ដែលកំពុង​ដំណើរការ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ពេលជិះ​យន្តហោះ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"អ៊ីនធឺណិតនឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"មិនមាន​ការតភ្ជាប់ទេ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"មិន​មាន​បណ្ដាញផ្សេងទៀតដែល​អាច​ប្រើ​បានទេ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"មិន​មាន​បណ្ដាញដែល​អាច​ប្រើ​បានទេ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ព័ត៌មាន​លម្អិត​អំពីបណ្ដាញ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ចុចលើបណ្ដាញណាមួយ ដើម្បីភ្ជាប់"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិន​អាច​ភ្ជាប់​បណ្ដាញ​បានទេ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 9b9138c..e48c842 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ಇದು ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳಿಗೆ ಹಾಗೂ ಸೇವೆಗಳಿಗೆ ನಿಮ್ಮ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುವುದಕ್ಕಾಗಿ ಇರುವ ಪ್ರವೇಶದ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ಇದು ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳಿಗೆ ಹಾಗೂ ಸೇವೆಗಳಿಗೆ ನಿಮ್ಮ ಕ್ಯಾಮರಾ ಅಥವಾ ಮೈಕ್ರೋಫೋನ್ ಬಳಸುವುದಕ್ಕಾಗಿ ಇರುವ ಪ್ರವೇಶದ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ಸಾಧನ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ತೆರೆಯುವುದಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ದೃಢೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ. ದೃಢೀಕರಿಸಲು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಫೋನ್ ಕರೆ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ಇಂಟರ್ನೆಟ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"ವೈ‑ಫೈ"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ನೆಟ್‌ವರ್ಕ್ ವಿವರಗಳು"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ಕನೆಕ್ಟ್ ಮಾಡಲು ಒಂದು ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 044086e..6f896c7 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"카메라를 사용할 수 있는 모든 앱 및 서비스에 대해 액세스가 차단 해제됩니다."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"카메라 또는 마이크를 사용할 수 있는 모든 앱 및 서비스에 대해 액세스가 차단 해제됩니다."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"기기"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"충전 중"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"지문으로 열기"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"인증이 필요합니다. 지문 센서를 터치하여 인증하세요."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"진행 중인 전화 통화"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"비행기 모드"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"인터넷에 자동으로 연결되지 않음"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"연결되지 않음"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"사용 가능한 다른 네트워크가 없음"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"사용 가능한 네트워크가 없음"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"네트워크 세부정보"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"연결하려면 네트워크를 탭하세요"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 9c2929d..20ec715 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Камераны колдонууга уруксат алган бардык колдонмолор менен кызматтар бөгөттөн чыгат."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Камераңызды же микрофонуңузду колдонууга уруксат алган бардык колдонмолор менен кызматтар бөгөттөн чыгат."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Түзмөк"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Кубатталууда"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Учурдагы телефон чалуу"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Учак режими"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматтык түрдө туташпайт"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыш жок"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Башка тармактар жеткиликсиз"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Тармактар жеткиликтүү эмес"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Тармактын чоо-жайы"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Кайсы тармакка туташасыз?"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 34bf28a..ee0e4b3 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -32,11 +32,11 @@
     -->
     <dimen name="qs_customize_header_min_height">48dp</dimen>
 
+    <!--  In landscape the security footer is actually part of the header,
+    and needs to be as short as the header  -->
     <dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
     <dimen name="qs_footer_padding">14dp</dimen>
-    <dimen name="qs_footers_margin_bottom">0dp</dimen>
     <dimen name="qs_security_footer_background_inset">12dp</dimen>
-    <dimen name="qs_security_footer_corner_radius">28dp</dimen>
 
     <dimen name="battery_detail_graph_space_top">9dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 53a0ace..de3afd3 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ນີ້ຈະຍົກເລີກການບລັອກການເຂົ້າເຖິງແອັບ ແລະ ບໍລິການທັງໝົດທີ່ອະນຸຍາດໃຫ້ໃຊ້ກ້ອງຖ່າຍຮູບຂອງທ່ານ."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ນີ້ຈະປົດບລັອກການເຂົ້າເຖິງແອັບ ແລະ ບໍລິການທັງໝົດທີ່ອະນຸຍາດໃຫ້ໃຊ້ກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນຂອງທ່ານ."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ອຸປະກອນ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ກຳລັງສາກໄຟ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ໃຊ້ລາຍນິ້ວມືເພື່ອເປີດ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ຕ້ອງພິສູດຢືນຢັນ. ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມືເພື່ອພິສູດຢືນຢັນ."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ສາຍໂທອອກ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ໂໝດຢູ່ໃນຍົນ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ຈະບໍ່ເຊື່ອມຕໍ່ອິນເຕີເນັດອັດຕະໂນມັດ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ບໍ່ມີການເຊື່ອມຕໍ່"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ບໍ່ມີເຄືອຂ່າຍອື່ນທີ່ສາມາດໃຊ້ໄດ້"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ບໍ່​ມ​ີ​ເຄືອ​ຂ່າຍ​ທີ່​ສາ​ມາດ​ໃຊ້​ໄດ້"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ລາຍລະອຽດເຄືອຂ່າຍ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ແຕະເຄືອຂ່າຍໃດໜຶ່ງເພື່ອເຊື່ອມຕໍ່"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 56f187e..230ee19 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tai atlikus visų programų ir paslaugų prieigos blokavimas panaikinamas ir joms leidžiama naudoti fotoaparatą."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tai atlikus visų programų ir paslaugų prieigos blokavimas panaikinamas ir joms leidžiama naudoti fotoaparatą ar mikrofoną."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Įrenginys"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Perbraukite aukštyn, kad perjungtumėte programas"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Vilkite į dešinę, kad greitai perjungtumėte programas"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Įkrautas"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Kraunamas"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Reikia nustatyti tapatybę. Nustatykite tapatybę palietę kontrolinio kodo jutiklį."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Vykstantis telefono skambutis"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Lėktuvo režimas"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Prie interneto nebus jungiamasi automatiškai"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nėra ryšio"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nėra kitų pasiekiamų tinklų"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nėra pasiekiamų tinklų"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Išsami tinklo informacija"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Palieskite tinklą, kad prisijungtumėte"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4522b98..c454f2a 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Visas lietotnes un pakalpojumi, kuriem ir atļauts izmantot kameru, varēs tai piekļūt."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Visas lietotnes un pakalpojumi, kuriem ir atļauts izmantot kameru vai mikrofonu, varēs tiem piekļūt."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Ierīce"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Velciet augšup, lai pārslēgtu lietotnes"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lai ātri pārslēgtu lietotnes, velciet pa labi"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Akumulators uzlādēts"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Notiek uzlāde"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Nepieciešama autentifikācija. Pieskarieties pirksta nospieduma sensoram, lai veiktu autentificēšanu."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Notiekošs tālruņa zvans"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Lidojuma režīms"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ir izveidots savienojums"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneta savienojums netiks izveidots automātiski"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nav savienojuma"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nav pieejams neviens cits tīkls"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nav pieejams neviens tīkls"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Dati par tīklu"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Pieskarieties tīklam, lai izveidotu savienojumu"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5317bca..fc1a69a 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ова ќе го одблокира пристапот за сите апликации и услуги на кои им е дозволено користење на камерата."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ова ќе го одблокира пристапот за сите апликации и услуги на кои им е дозволено користење на камерата или микрофонот."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Уред"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Се полни"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Тековен телефонски повик"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Авионски режим"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Не може автоматски да се поврзе на интернет"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нема интернет-врска"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нема други достапни мрежи"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Нема достапни мрежи"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Детали за мрежата"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Допрете на мрежа за да се поврзете"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e798c7d..1c3837f 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"നിങ്ങളുടെ ക്യാമറ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്ന എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കുമുള്ള ആക്‌സസ് ഇത് അൺബ്ലോക്ക് ചെയ്യുന്നു."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"നിങ്ങളുടെ ക്യാമറയോ മൈക്രോഫോണോ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്ന എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കുമുള്ള ആക്‌സസ് ഇത് അൺബ്ലോക്ക് ചെയ്യുന്നു."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ഉപകരണം"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ചാർജ് ചെയ്യുന്നു"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"തുറക്കുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. പരിശോധിച്ചുറപ്പിക്കാൻ, വിരലടയാള സെൻസറിൽ സ്‌പർശിക്കുക."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"സജീവമായ ഫോൺ കോൾ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ഫ്ലൈറ്റ് മോഡ്"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ഇന്റർനെറ്റ് സ്വയമേവ കണക്‌റ്റ് ചെയ്യില്ല"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"കണക്ഷനില്ല"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"മറ്റ് നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"വൈഫൈ"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"നെറ്റ്‌വർക്ക് വിശദാംശങ്ങൾ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"കണക്‌റ്റ് ചെയ്യാൻ ഒരു നെറ്റ്‌വർക്കിൽ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്‌വർക്കുകൾ തിരയുന്നു…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്‌വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0e4cac02..4e04c61 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Энэ нь таны камерыг ашиглах зөвшөөрөлтэй бүх апп болон үйлчилгээний хандалтыг блокоос гаргана."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Энэ нь таны камер эсвэл микрофоныг ашиглах зөвшөөрөлтэй бүх апп болон үйлчилгээний хандалтыг блокоос гаргана."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Төхөөрөмж"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Цэнэглэж байна"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Нээхийн тулд хурууны хээг ашиглана уу"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Баталгаажуулалт шаардлагатай. Баталгаажуулахын тулд хурууны хээ мэдрэгчид хүрнэ үү."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Үргэлжилж буй утасны дуудлага"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Нислэгийн горим"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернэт автоматаар холбогдохгүй"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Холболт алга"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Өөр боломжтой сүлжээ байхгүй байна"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Боломжтой сүлжээ байхгүй байна"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Сүлжээний дэлгэрэнгүй мэдээлэл"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Холбогдохын тулд сүлжээг товшино уу"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 45a9a1b..5785b41 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"हे तुमचा कॅमेरा वापरण्याची परवानगी असलेल्या सर्व ॲप्स आणि सेवांसाठी अ‍ॅक्सेस अनब्लॉक करते."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"हे तुमचा कॅमेरा आणि मायक्रोफोन वापरण्याची परवानगी असलेल्या सर्व ॲप्स व सेवांसाठी अ‍ॅक्सेस अनब्लॉक करते."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"डिव्हाइस"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज होत आहे"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"उघडण्यासाठी फिंगरप्रिंट वापरा"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ऑथेंटिकेशन आवश्यक आहे. ऑथेंटिकेट करण्यासाठी फिंगरप्रिंट सेन्सरला स्पर्श करा."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फोन कॉल सुरू आहे"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"विमान मोड"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट ऑटो-कनेक्ट होणार नाही"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"कोणतेही नेटवर्क उपलब्‍ध नाही"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"वाय-फाय"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कचे तपशील"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"कनेक्ट करण्यासाठी नेटवर्कवर टॅप करा"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्‍ट करता आले नाही"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 2349e28..6b6a6b7 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera anda."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera atau mikrofon anda."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Peranti"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Leret ke atas untuk menukar apl"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Seret ke kanan untuk beralih apl dengan pantas"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Sudah dicas"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Mengecas"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan cap jari untuk membuka"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Pengesahan diperlukan. Sentuh penderia cap jari untuk pengesahan."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telefon yang sedang berjalan"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Mod pesawat"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Disambungkan"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan bersambung secara automatik"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tiada sambungan"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Tiada rangkaian lain yang tersedia"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Tiada rangkaian tersedia"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Butiran rangkaian"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketik rangkaian untuk membuat sambungan"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 23de47f..ba643f0 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"၎င်းက သင့်ကင်မရာသုံးရန် ခွင့်ပြုထားသော အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် သုံးခွင့်ကို ပြန်ဖွင့်ပေးသည်။"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"၎င်းက သင့်ကင်မရာ (သို့) မိုက်ခရိုဖုန်းသုံးရန် ခွင့်ပြုထားသော အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် သုံးခွင့်ကို ပြန်ဖွင့်ပေးသည်။"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"စက်"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"အားသွင်းနေ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ဖွင့်ရန် လက်ဗွေကို သုံးပါ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"အထောက်အထားစိစစ်ခြင်း လိုအပ်သည်။ အထောက်အထားစိစစ်ရန် လက်ဗွေ အာရုံခံကိရိယာကို ထိပါ။"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"လက်ရှိ ဖုန်းခေါ်ဆိုမှု"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"လေယာဉ်ပျံမုဒ်"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"အင်တာနက်က အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ချိတ်ဆက်မှုမရှိပါ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"အခြားကွန်ရက်များ မရှိပါ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ကွန်ရက်များ မရှိပါ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ကွန်ရက် အသေးစိတ်များ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ချိတ်ဆက်ရန် ကွန်ရက်ကို တို့ပါ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 9c18b95..7a42d3e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dette opphever blokkeringen av tilgang for alle apper og tjenester som har tillatelse til å bruke kameraet."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dette opphever blokkeringen av tilgang for alle apper og tjenester som har tillatelse til å bruke kameraet eller mikrofonen."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Sveip opp for å bytte apper"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Dra til høyre for å bytte apper raskt"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Oppladet"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Lader"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående telefonsamtale"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Flymodus"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internett kobles ikke til automatisk"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen tilkobling"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ingen andre nettverk er tilgjengelige"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Ingen nettverk er tilgjengelige"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Nettverksdetaljer"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trykk på et nettverk for å koble til"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9ae2d43e2..c46d705 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"यन्त्र"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हुँदै छ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"फिंगरप्रिन्ट प्रयोग गरी खोल्नुहोस्"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि गर्नु पर्ने हुन्छ। पुष्टि गर्न फिंगरप्रिन्ट सेन्सर छुनुहोस्।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"जारी फोन कल"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"हवाइजहाज मोड"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"इन्टरनेट स्वतः कनेक्ट हुँदैन"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"इन्टरनेट छैन"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"अन्य नेटवर्क उपलब्ध छैनन्"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"कुनै पनि नेटवर्क उपलब्ध छैन"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कसम्बन्धी विवरण"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इन्टरनेट कनेक्ट गर्न कुनै नेटवर्कमा ट्याप गर्नुहोस्"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 155d1ad..f0cc269 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Hiermee hef je de toegangsblokkering op voor alle apps en services die rechten hebben om je camera te gebruiken."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Hiermee hef je de toegangsblokkering op voor alle apps en services die rechten hebben om je camera of microfoon te gebruiken."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Apparaat"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swipe omhoog om te schakelen tussen apps"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Sleep naar rechts om snel tussen apps te schakelen"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Opgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Opladen"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om te openen"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Verificatie vereist. Raak de vingerafdruksensor aan om de verificatie uit te voeren."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Actief telefoongesprek"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet maakt niet automatisch verbinding"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen andere netwerken beschikbaar"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerken beschikbaar"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkgegevens"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op een netwerk om verbinding te maken"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4d44690b..2fcc505 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ଆପଣଙ୍କ କ୍ୟାମେରାକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇଥିବା ସମସ୍ତ ଆପ୍ ଓ ସେବା ପାଇଁ ଏହା ଆକ୍ସେସକୁ ଅନବ୍ଲକ୍ କରେ।"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ଆପଣଙ୍କ କ୍ୟାମେରା କିମ୍ବା ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇଥିବା ସମସ୍ତ ଆପ୍ ଓ ସେବା ପାଇଁ ଏହା ଆକ୍ସେସକୁ ଅନବ୍ଲକ୍ କରେ।"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ଡିଭାଇସ୍"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ଚାର୍ଜ କରାଯାଉଛି"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ଚାଲୁଥିବା ଫୋନ୍ କଲ୍"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ଏୟାରପ୍ଲେନ୍ ମୋଡ୍"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ଇଣ୍ଟରନେଟ୍ ସ୍ଵତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"ୱାଇ-ଫାଇ"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ନେଟୱାର୍କ ବିବରଣୀ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ସଂଯୋଗ କରିବାକୁ ଏକ ନେଟୱାର୍କରେ ଟାପ୍ କରନ୍ତୁ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 7bb60a0..3dad773 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ਇਹ ਉਹਨਾਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਪਹੁੰਚ ਨੂੰ ਅਣਬਲਾਕ ਕਰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਹਾਡਾ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ਇਹ ਉਹਨਾਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਪਹੁੰਚ ਨੂੰ ਅਣਬਲਾਕ ਕਰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਹਾਡਾ ਕੈਮਰਾ ਜਾਂ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"ਡੀਵਾਈਸ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ਚਾਰਜ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ਖੋਲ੍ਹਣ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ਪ੍ਰਮਾਣੀਕਰਨ ਲੋੜੀਂਦਾ ਹੈ। ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ਜਾਰੀ ਫ਼ੋਨ ਕਾਲ"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ਇੰਟਰਨੈੱਟ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ਕੋਈ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"ਵਾਈ-ਫਾਈ"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ਨੈੱਟਵਰਕ ਵੇਰਵੇ"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ਕਨੈਕਟ ਕਰਨ ਲਈ ਕਿਸੇ ਨੈੱਟਵਰਕ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index ea6f225..30d6f05 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Spowoduje to odblokowanie dostępu dla wszystkich aplikacji i usług, które mają uprawnienia do korzystania z aparatu."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Spowoduje to odblokowanie dostępu dla wszystkich aplikacji i usług, które mają uprawnienia do korzystania z aparatu lub mikrofonu."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Urządzenie"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Przesuń w górę, by przełączyć aplikacje"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Szybko przeciągnij w prawo, by przełączyć aplikacje"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Naładowana"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Ładowanie"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Wymagane uwierzytelnienie. Dotknij czytnika liniii papilarnych, by uwierzytelnić."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktywne połączenie"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Tryb samolotowy"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Połączono"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nie połączy się automatycznie z internetem"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Brak połączenia"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Brak innych dostępnych sieci"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Brak dostępnych sieci"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Szczegóły sieci"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Kliknij sieć, aby połączyć"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2f1501b..24015d8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera ou seu microfone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Deslize para cima para mudar de app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arraste para a direita para mudar rapidamente de app"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregado"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Carregando"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 563e99d..362d656 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Isto desbloqueia o acesso a todas as apps e serviços com autorização para utilizar a sua câmara."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Isto desbloqueia o acesso a todas as apps e serviços com autorização para utilizar a sua câmara ou microfone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Deslizar rapidamente para cima para mudar de app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arraste para a direita para mudar rapidamente de app."</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"A carregar"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilize a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação necessária. Toque no sensor de impressões digitais para autenticar."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica em curso"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo de avião"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não estabelece ligação automaticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem ligação"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Sem redes disponíveis"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque numa rede para estabelecer ligação"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2f1501b..24015d8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera ou seu microfone."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Deslize para cima para mudar de app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arraste para a direita para mudar rapidamente de app"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregado"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Carregando"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2102600..452664f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera sau microfonul."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispozitiv"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Glisați în sus pentru a comuta între aplicații"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Glisați la dreapta pentru a comuta rapid între aplicații"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comutați secțiunea Recente"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Încărcată"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Se încarcă"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosiți amprenta ca să deschideți"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atingeți senzorul de amprentă pentru a vă autentifica."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Apel telefonic în desfășurare"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modul Avion"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nu se conectează automat la internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nicio conexiune"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nicio rețea disponibilă"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detalii despre rețea"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Atingeți o rețea pentru a vă conecta"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index eeb1f3e..7ebbb43 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры или микрофона."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарядка батареи"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущий вызов"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Режим полета"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Без автоподключения к интернету"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нет подключения к интернету"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Нет доступных сетей"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Сведения о сети"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Выберите сеть, чтобы подключиться"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 247b7a5..6cf9f9b 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"මෙය ඔබගේ කැමරාව භාවිතා කිරීමට ඉඩ දී ඇති සියලු යෙදුම් සහ සේවා සඳහා ප්‍රවේශය අවහිර කිරීම ඉවත් කරයි."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"මෙය ඔබගේ කැමරාව හෝ මයික්‍රෆෝනය භාවිත කිරීමට ඉඩ දී ඇති සියලු යෙදුම් සහ සේවා සඳහා ප්‍රවේශය අවහිර කිරීම ඉවත් කරයි."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"උපාංගය"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ආරෝපණය වෙමින්"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"විවෘත කිරීමට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්‍යාපනය අවශ්‍යයි. සත්‍යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්‍රියාත්මක වන දුරකථන ඇමතුම"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ගුවන් යානා ප්‍රකාරය"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"අන්තර්ජාලය ස්වයංක්‍රියව සම්බන්ධ නොවනු ඇත"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"සම්බන්ධතාවයක් නැත"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ලබා ගත හැකි වෙනත් ජාල නැත"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ජාලය නොතිබේ"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"ජාල විස්තර"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"සම්බන්ධ වීමට ජාලයක් තට්ටු කරන්න"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 74b24bd..5893223 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Táto akcia odblokuje prístup všetkým aplikáciám a službám, ktoré majú povolené používať fotoaparát."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Táto akcia odblokuje prístup všetkým aplikáciám a službám, ktoré majú povolené používať fotoaparát alebo mikrofón."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Zariadenie"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Potiahnutím nahor prepnete aplikácie"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Presunutím doprava rýchlo prepnete aplikácie"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Nabitá"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nabíja sa"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorte odtlačkom prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vyžaduje sa overenie. Dotknite sa senzora odtlačkov prstov."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Prebiehajúci telefonický hovor"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Režim v lietadle"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sa nepripojí automaticky"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bez pripojenia"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nie sú k dispozícii žiadne ďalšie siete"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nie sú k dispozícii žiadne siete"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti siete"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ak sa chcete pripojiť, klepnite na sieť"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index cfb89d4..6bc27d0 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"S tem boste odblokirali dostop za vse aplikacije in storitve, ki imajo dovoljenje za uporabo fotoaparata."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"S tem boste odblokirali dostop za vse aplikacije in storitve, ki imajo dovoljenje za uporabo fotoaparata ali mikrofona."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Naprava"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Za preklop aplikacij povlecite navzgor"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Povlecite v desno za hiter preklop med aplikacijami"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Baterija napolnjena"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Polnjenje"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Odprite s prstnim odtisom"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Zahtevano je preverjanje pristnosti. Za preverjanje pristnosti se dotaknite tipala prstnih odtisov."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Poteka klic"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Način za letalo"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Samodejna povezava z internetom ni mogoča."</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ni povezave"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nobeno drugo omrežje ni na voljo"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Na voljo ni nobeno omrežje"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Podatki o omrežju"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Za vzpostavitev povezave se dotaknite omrežja."</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f7753c7..1600d03 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kjo zhbllokon qasjen për të gjitha aplikacionet dhe shërbimet që lejohen të përdorin kamerën tënde."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kjo zhbllokon qasjen për të gjitha aplikacionet dhe shërbimet që lejohen të përdorin kamerën ose mikrofonin tënd."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Pajisja"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rrëshqit shpejt lart për të ndërruar aplikacionet"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zvarrit djathtas për të ndërruar aplikacionet me shpejtësi"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"I karikuar"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Po karikohet"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kërkohet vërtetimi. Prek sensorin e gjurmës së gishtit për t\'u vërtetuar."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonatë në vazhdim"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Modaliteti i aeroplanit"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Lidhur"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneti nuk do të lidhet automatikisht"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Nuk ofrohet asnjë rrjet"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Detajet e rrjetit"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trokit te një rrjet për t\'u lidhur"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 73ecc4c..0670a9c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -435,6 +435,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Овим ће се одблокирати приступ за све апликације и услуге које имају дозволу за коришћење камере."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Овим ће се одблокирати приступ за све апликације и услуге које имају дозволу за коришћење камере или микрофона."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Уређај"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Пуни се"</string>
@@ -1164,4 +1166,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Отворите помоћу отиска прста"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна је потврда идентитета. Додирните сензор за отисак прста да бисте потврдили идентитет."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Актуелни телефонски позив"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Режим рада у авиону"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аутоматско повезивање на интернет није могуће"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Веза није успостављена"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Није доступна ниједна друга мрежа"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Нема доступних мрежа"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Детаљи о мрежи"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Додирните мрежу да бисте се повезали"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 1f60b3a..4ba8999 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Detta återaktiverar åtkomsten för alla appar och tjänster som tillåts att använda kameran."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Detta återaktiverar åtkomsten för alla appar och tjänster som tillåts att använda kameran eller mikrofonen."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Byt appar genom att svepa uppåt"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tryck och dra åt höger för att snabbt byta mellan appar"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Laddat"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laddar"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering krävs. Identifiera dig genom att trycka på fingeravtryckssensorn."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående samtal"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Flygplansläge"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Du ansluts inte automatiskt till internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen anslutning"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Inga andra nätverk är tillgängliga"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Det finns inga tillgängliga nätverk"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Nätverksinformation"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryck på ett nätverk för att ansluta"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7ac26be4..0ba5d05 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Hatua hii huruhusu programu na huduma zote zenye idhini zitumie kamera yako."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Hatua hii huruhusu programu na huduma zote zenye idhini zitumie kamera au maikrofoni yako."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Kifaa"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Telezesha kidole juu ili ubadilishe programu"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Buruta kulia ili ubadilishe programu haraka"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Betri imejaa"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Inachaji"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Simu inayoendelea"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Hali ya ndegeni"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Intaneti haitaunganishwa kiotomatiki"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Hakuna muunganisho"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Hakuna mitandao mingine inayopatikana"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Hakuna mitandao inayopatikana"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Maelezo ya mtandao"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Gusa mtandao ili uunganishe"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index e2b2e25..369e45c 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -18,6 +18,10 @@
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">2</integer>
 
+    <integer name="quick_settings_num_columns">2</integer>
+    <integer name="quick_qs_panel_max_rows">4</integer>
+    <integer name="quick_qs_panel_max_tiles">8</integer>
+
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 2f5e8ea..942cb2b 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -38,4 +38,6 @@
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">4</integer>
 
+    <!-- How many lines to show in the security footer -->
+    <integer name="qs_security_footer_maxLines">1</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index da80b85..527537b 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -95,4 +95,9 @@
     <dimen name="controls_top_margin">24dp</dimen>
 
     <dimen name="global_actions_grid_item_layout_height">80dp</dimen>
+
+    <!--  For large screens the security footer appears below the footer,
+    same as phones in portrait  -->
+    <dimen name="qs_security_footer_single_line_height">48dp</dimen>
+    <dimen name="qs_security_footer_background_inset">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 426ddd4..4de2ab5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"உங்கள் கேமராவைப் பயன்படுத்த அனுமதிக்கப்பட்டுள்ள அனைத்து ஆப்ஸ் மற்றும் சேவைகளை அணுகுவதற்கான தடுப்பை இது நீக்கும்."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"உங்கள் கேமராவையோ மைக்ரோஃபோனையோ பயன்படுத்த அனுமதிக்கப்பட்டுள்ள அனைத்து ஆப்ஸ் மற்றும் சேவைகளை அணுகுவதற்கான தடுப்பை இது நீக்கும்."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"சாதனம்"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"சார்ஜ் ஆகிறது"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"செயலில் உள்ள மொபைல் அழைப்பு"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"விமானப் பயன்முறை"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"இணையத்துடன் தானாகவே இணைக்காது"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"இணைப்பு இல்லை"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"வைஃபை"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"நெட்வொர்க் விவரங்கள்"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"இணையத்துடன் இணைய நெட்வொர்க்கைத் தட்டுங்கள்"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 020d9b4..6cdaa22 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లు, సర్వీస్‌లకు యాక్సెస్‌ను ఇది అన్‌బ్లాక్ చేస్తుంది."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"మీ కెమెరాను లేదా మైక్రోఫోన్‌ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లు, సర్వీస్‌లకు యాక్సెస్‌ను ఇది అన్‌బ్లాక్ చేస్తుంది."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"పరికరం"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"ఛార్జ్ అవుతోంది"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"తెరవడానికి వేలిముద్రను ఉపయోగించండి"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ప్రామాణీకరణ అవసరం. ప్రామాణీకరించడానికి వేలిముద్ర సెన్సార్‌ను తాకండి."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ఫోన్ కాల్ జరుగుతోంది"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"విమానం మోడ్"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"ఇంటర్నెట్ ఆటోమెటిక్‌గా కనెక్ట్ అవ్వదు"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్‌వర్క్‌లేవీ అందుబాటులో లేవు"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"నెట్‌వర్క్‌లు అందుబాటులో లేవు"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"నెట్‌వర్క్ వివరాలు"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"కనెక్ట్ చేయడానికి నెట్‌వర్క్‌ను ట్యాప్ చేయండి"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్‌వర్క్‌ల కోసం సెర్చ్ చేస్తోంది…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్‌వర్క్‌కు కనెక్ట్ చేయడం విఫలమైంది"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index ce469f7..f2fce1e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"การดำเนินการนี้จะเลิกบล็อกสิทธิ์เข้าถึงของแอปและบริการทั้งหมดที่ได้รับอนุญาตให้ใช้กล้องของคุณ"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"การดำเนินการนี้จะเลิกบล็อกสิทธิ์เข้าถึงของแอปและบริการทั้งหมดที่ได้รับอนุญาตให้ใช้กล้องหรือไมโครโฟนของคุณ"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"อุปกรณ์"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"กำลังชาร์จ"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ใช้ลายนิ้วมือเพื่อเปิด"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ต้องมีการตรวจสอบสิทธิ์ แตะเซ็นเซอร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"กำลังโทรอยู่"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"โหมดบนเครื่องบิน"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"อินเทอร์เน็ตจะไม่เชื่อมต่ออัตโนมัติ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ไม่มีการเชื่อมต่อ"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ไม่มีเครือข่ายอื่นๆ ที่พร้อมใช้งาน"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"ไม่มีเครือข่ายที่พร้อมใช้งาน"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"รายละเอียดเครือข่าย"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"แตะเครือข่ายเพื่อเชื่อมต่อ"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8d0a847..3ef994e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ina-unblock nito ang access para sa lahat ng app at serbisyong pinapayagang gumamit ng iyong camera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ina-unblock nito ang access para sa lahat ng app at serbisyong pinapayagang gumamit ng iyong camera o mikropono."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Mag-swipe pataas upang lumipat ng app"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"I-drag pakanan para mabilisang magpalipat-lipat ng app"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Tapos nang mag-charge"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nagcha-charge"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gamitin ang fingerprint para buksan"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kailangan ng pag-authenticate. Pindutin ang sensor para sa fingerprint para mag-authenticate."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Kasalukuyang may tawag sa telepono"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Airplane mode"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Hindi awtomatikong kokonekta ang Internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Walang koneksyon"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Walang available na iba pang network"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Walang available na network"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Mga detalye ng network"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Mag-tap ng network para kumonekta"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6b55d1c..d9afda18 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Bu işlem, kameranızı kullanmasına izin verilen tüm uygulama ve hizmetlere erişimin engellemesini kaldırır."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Bu işlem, kamera veya mikrofonunuzu kullanmasına izin verilen tüm uygulama ve hizmetlere erişimin engellemesini kaldırır."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Uygulamalar arasında geçiş yapmak için yukarı kaydırın"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Uygulamaları hızlıca değiştirmek için sağa kaydırın"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Şarj oldu"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Şarj oluyor"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Devam eden telefon görüşmesi"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Uçak modu"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Bağlı"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternete otomatik olarak bağlanmaz"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yok"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Kullanılabilir başka ağ yok"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Kullanılabilir ağ yok"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Kablosuz"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Ağ bilgileri"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Bağlanmak için bir ağa dokunun"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 0a5992b..50a0f52 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -437,6 +437,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Усі додатки та сервіси, яким дозволено користуватися вашою камерою, отримають доступ."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Усі додатки та сервіси, яким дозволено користуватися вашою камерою чи мікрофоном, отримають доступ."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Пристрій"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Заряджається"</string>
@@ -1170,4 +1172,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Активний телефонний виклик"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Режим польоту"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Автоматичне інтернет-з’єднання вимкнено"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Немає з\'єднання"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Інші мережі недоступні"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Немає доступних мереж"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Деталі мережі"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Натисніть мережу, до якої потрібно підключитися"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 5a96fa8..62b09f2 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"اس سے آپ کا کیمرا استعمال کرنے کے لیے اجازت یافتہ سبھی ایپس اور سروسز کے لیے رسائی غیر مسدود ہو جاتی ہے۔"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"اس سے آپ کا کیمرا یا مائیکروفون استعمال کرنے کے لیے اجازت یافتہ سبھی ایپس اور سروسز کے لیے رسائی غیر مسدود ہو جاتی ہے۔"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"آلہ"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"چارج ہو رہی ہے"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"کھولنے کے لیے فنگر پرنٹ کا استعمال کریں"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"توثیق مطلوب ہے۔ توثیق کرنے کے لیے فنگر پرنٹ سینسر کو ٹچ کریں۔"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"جاری فون کال"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"ہوائی جہاز وضع"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"انٹرنیٹ خود کار طور پر منسلک نہیں ہوگا"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"کوئی نیٹ ورکس دستیاب نہیں ہیں"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"نیٹ ورک کی تفصیلات"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"منسلک کرنے کے لیے نیٹ ورک پر تھپتھپائیں"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index d96ea064..fae90c8 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamerangizdan foydalanishga ruxsat berilgan barcha ilovalar va xizmatlar uchun ruxsatni blokdan chiqaradi."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera va mikrofoningizdan foydalanishga ruxsat berilgan barcha ilovalar va xizmatlar uchun ruxsatni blokdan chiqaradi."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Qurilma"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Ilovalarni almashtirish uchun ekranni tepaga suring"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Ilovalarni tezkor almashtirish uchun o‘ngga torting"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Quvvat oldi"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Quvvat olmoqda"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ochish uchun barmoq izidan foydalaning"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Haqiqiylikni tekshirish talab etiladi. Autentifikatsiya uchun barmoq izi skaneriga tegining."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Joriy telefon chaqiruvi"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Parvoz rejimi"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ulandi"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet avtomatik ravishda ulanmaydi"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Hech qanday tarmoq mavjud emas"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Tarmoq tafsilotlari"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ulanish uchun tarmoq ustiga bosing"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 54b4967..1ef805f 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Thao tác này sẽ bỏ chặn quyền truy cập cho mọi ứng dụng và dịch vụ được phép sử dụng máy ảnh của bạn."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Thao tác này sẽ bỏ chặn quyền truy cập cho mọi ứng dụng và dịch vụ được phép sử dụng máy ảnh hoặc micrô của bạn."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Thiết bị"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vuốt lên để chuyển đổi ứng dụng"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Kéo sang phải để chuyển đổi nhanh giữa các ứng dụng"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Đã sạc đầy"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Đang sạc"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Bạn cần phải xác thực. Hãy chạm vào cảm biến vân tay để xác thực."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Đang gọi điện thoại"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Chế độ trên máy bay"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Đã kết nối"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"Sẽ không tự động kết nối Internet"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Không có kết nối mạng"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Không có mạng nào khác"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Không có mạng"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Thông tin chi tiết về mạng"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Nhấn vào một mạng để kết nối"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 3cbcf31..a8b8285 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"这将会为所有获准使用您摄像头的应用和服务启用这项权限。"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"这将会为所有获准使用您的摄像头或麦克风的应用和服务启用这项权限。"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"设备"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"正在充电"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"正在进行通话"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"飞行模式"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"不会自动连接到互联网"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"没有其他可用网络"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"没有可用网络"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"网络详情"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"点按要连接的网络"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 726947f..9e498f6 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"解除封鎖後,凡有存取權的應用程式和服務都可使用您的相機。"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"解除封鎖後,凡有存取權的應用程式和服務都可使用您的相機或麥克風。"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電中"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"飛行模式"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連線至互聯網"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有連線"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網絡"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網絡"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"網絡詳細資料"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕按網絡以連線"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 437bece..1b0fa10 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"這麼做可允許所有應用程式和服務使用相機。"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"這麼做可允許所有應用程式和服務使用相機或麥克風。"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</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>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電中"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"飛航模式"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連上網際網路"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有網路連線"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網路"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網路"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"網路詳細資料"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕觸要連線的網路"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6b7d577..a14229a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -433,6 +433,8 @@
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Lokhu kuvulela ukufinyelela kwawo wonke ama-app namasevisi avunyelwe ukusebenzisa ikhamera yakho."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Lokhu kuvulela ukufinyelela kwawo wonke ama-app namasevisi avunyelwe ukusebenzisa ikhamera yakho noma imakrofoni."</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Idivayisi"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swayiphela phezulu ukuze ushintshe izinhlelo zokusebenza"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Hudula ngqo ukuze ushintshe ngokushesha izinhlelo zokusebenza"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Kushajiwe"</string>
     <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Iyashaja"</string>
@@ -1158,4 +1160,18 @@
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ikholi yefoni eqhubekayo"</string>
+    <string name="airplane_mode" msgid="2536350001462130668">"Imodi yendiza"</string>
+    <string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
+    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+    <string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
+    <string name="mobile_data_off_summary" msgid="5621158216585822679">"I-inthanethi ngeke ixhumeke ngokuzenzakalelayo"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"Alukho uxhumano"</string>
+    <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Awekho amanye amanethiwekhi atholakalayo"</string>
+    <string name="all_network_unavailable" msgid="4112774339909373349">"Awekho amanethiwekhi atholakalayo"</string>
+    <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+    <string name="pref_title_network_details" msgid="1639455355897668883">"Imininingwane yenethiwekhi"</string>
+    <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Thepha inethiwekhi ukuze uxhume"</string>
+    <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
+    <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
+    <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index b5337d3..3121ce3 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -69,6 +69,10 @@
     <declare-styleable name="DateView">
         <attr name="datePattern" format="string" />
     </declare-styleable>
+    <declare-styleable name="VariableDateView">
+        <attr name="longDatePattern" format="string" />
+        <attr name="shortDatePattern" format="string" />
+    </declare-styleable>
     <declare-styleable name="PseudoGridView">
         <attr name="numColumns" format="integer" />
         <attr name="verticalSpacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2260d21..bbbaf65 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 -->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <drawable name="notification_number_text_color">#ffffffff</drawable>
     <drawable name="ticker_background_color">#ff1d1d1d</drawable>
     <drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
@@ -281,4 +281,16 @@
     <color name="wallet_card_border">#33FFFFFF</color>
 
     <color name="people_tile_background">@android:color/system_accent2_50</color>
+
+    <!-- Internet Dialog -->
+    <!-- Material next state on color-->
+    <color name="settingslib_state_on_color">?androidprv:attr/colorAccentPrimary</color>
+    <!-- Material next state off color-->
+    <color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
+    <!-- Material next track on color-->
+    <color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
+    <!-- Material next track off color-->
+    <color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
+    <color name="connected_network_primary_color">#ff000000</color>
+    <color name="connected_network_tertiary_color">#808080</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f5d47ce..bf29cf4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -86,7 +86,10 @@
     <bool name="config_navigation_bar_enable_auto_dim_no_visible_wallpaper">true</bool>
 
     <!-- The maximum number of tiles in the QuickQSPanel -->
-    <integer name="quick_qs_panel_max_columns">4</integer>
+    <integer name="quick_qs_panel_max_tiles">4</integer>
+
+    <!-- The maximum number of rows in the QuickQSPanel -->
+    <integer name="quick_qs_panel_max_rows">2</integer>
 
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">2</integer>
@@ -603,8 +606,6 @@
 
     <!-- Whether wallet view is shown in landscape / seascape orientations -->
     <bool name="global_actions_show_landscape_wallet_view">false</bool>
-    <!-- Whether global actions should show an informational message about changes in S -->
-    <bool name="global_actions_show_change_info">false</bool>
 
     <!-- Package name of the preferred system app to perform eSOS action -->
     <string name="config_preferredEmergencySosPackage" translatable="false"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7485ef8..319ffd2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -62,7 +62,9 @@
     <item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
 
     <dimen name="floating_rotation_button_diameter">40dp</dimen>
-    <dimen name="floating_rotation_button_min_margin">4dp</dimen>
+    <dimen name="floating_rotation_button_min_margin">20dp</dimen>
+    <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
+    <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
 
     <!-- Height of notification icons in the status bar -->
     <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
@@ -402,8 +404,11 @@
     <dimen name="status_bar_header_padding_bottom">48dp</dimen>
 
     <!-- The height of the container that holds the battery and time in the quick settings header.
+    Preferred over using "@*android:dimen/quick_qs_offset_height" as system icons are not always
+    present in quick settings (e.g. in split shade) and it's useful to be able to override this
+    value in such cases.
          -->
-    <dimen name="qs_header_system_icons_area_height">48dp</dimen>
+    <dimen name="qs_header_system_icons_area_height">@*android:dimen/quick_qs_offset_height</dimen>
 
     <!-- How far the quick-quick settings panel extends below the status bar -->
     <dimen name="qs_quick_header_panel_height">128dp</dimen>
@@ -455,6 +460,10 @@
     <!-- Width for the notification panel and related windows -->
     <dimen name="match_parent">-1px</dimen>
 
+    <!-- Height of status bar in split shade mode - visible only on large screens -->
+    <dimen name="split_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+    <dimen name="split_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
+
     <!-- The top margin of the panel that holds the list of notifications. -->
     <dimen name="notification_panel_margin_top">0dp</dimen>
 
@@ -623,6 +632,7 @@
     <dimen name="qs_footer_icon_size">20dp</dimen>
     <dimen name="qs_header_top_padding">15dp</dimen>
     <dimen name="qs_header_bottom_padding">14dp</dimen>
+    <dimen name="qs_header_row_min_height">48dp</dimen>
 
     <dimen name="qs_footer_padding">20dp</dimen>
     <dimen name="qs_security_footer_height">88dp</dimen>
@@ -1124,7 +1134,6 @@
     <dimen name="global_actions_button_padding">38dp</dimen>
     <dimen name="global_actions_corner_radius">28dp</dimen>
     <dimen name="global_actions_lite_padding">24dp</dimen>
-    <dimen name="global_actions_info_margin">32dp</dimen>
 
     <!-- The maximum offset in either direction that elements are moved horizontally to prevent
          burn-in on AOD. -->
@@ -1266,6 +1275,8 @@
     <!--  Three privacy items. This value must not be exceeded  -->
     <dimen name="ongoing_appops_chip_max_width">76dp</dimen>
     <dimen name="ongoing_appops_dot_diameter">6dp</dimen>
+    <!--  Total minimum padding to enforce to ensure that the dot can always show  -->
+    <dimen name="ongoing_appops_dot_min_padding">20dp</dimen>
 
     <dimen name="ongoing_appops_dialog_side_margins">@dimen/notification_shade_content_margin_horizontal</dimen>
 
@@ -1579,4 +1590,35 @@
     <!-- The padding between the icon and the text. -->
     <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
     <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+
+    <!-- Internet panel related dimensions -->
+    <dimen name="internet_dialog_list_margin">12dp</dimen>
+    <dimen name="internet_dialog_list_max_height">614dp</dimen>
+
+    <!-- Signal icon in internet dialog -->
+    <dimen name="signal_strength_icon_size">24dp</dimen>
+
+    <!-- Internet dialog related dimensions -->
+    <dimen name="internet_dialog_corner_radius">24dp</dimen>
+
+    <!-- Size of internet dialog -->
+    <dimen name="settingslib_switchbar_margin">16dp</dimen>
+    <!-- Minimum width of switch -->
+    <dimen name="settingslib_min_switch_width">48dp</dimen>
+    <!-- Size of layout margin left -->
+    <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
+    <!-- Size of layout margin right -->
+    <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
+    <!-- Radius of switch bar -->
+    <dimen name="settingslib_switch_bar_radius">24dp</dimen>
+    <!-- Margin of switch thumb -->
+    <dimen name="settingslib_switch_thumb_margin">4dp</dimen>
+    <!-- Size of switch thumb -->
+    <dimen name="settingslib_switch_thumb_size">16dp</dimen>
+    <!-- Width of switch track -->
+    <dimen name="settingslib_switch_track_width">48dp</dimen>
+    <!-- Height of switch track -->
+    <dimen name="settingslib_switch_track_height">24dp</dimen>
+    <!-- Radius of switch track -->
+    <dimen name="settingslib_switch_track_radius">31dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index efa8754..c2b87a5 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -24,9 +24,6 @@
 
     <bool name="flag_monet">false</bool>
 
-    <!-- b/171917882 -->
-    <bool name="flag_notification_twocolumn">false</bool>
-
     <!-- AOD/Lockscreen alternate layout -->
     <bool name="flag_keyguard_layout">true</bool>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5aff817..ec951ff 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2983,8 +2983,36 @@
     <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
     <string name="ongoing_phone_call_content_description">Ongoing phone call</string>
 
-    <!-- Placeholder for string describing changes in global actions -->
-    <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
-    <!-- URL for more information about changes in global actions -->
-    <string name="global_actions_change_url" translatable="false"></string>
+    <!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
+    <string name="airplane_mode">Airplane mode</string>
+    <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
+    <string name="mobile_data_settings_title">Mobile data</string>
+    <!-- Provider Model: Summary text separator for preferences including a short description
+         (eg. "Connected / 5G"). [CHAR LIMIT=50] -->
+    <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
+    <!-- Provider Model:
+         Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_connection_active">Connected</string>
+    <!-- Provider Model:
+     Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
+    <!-- Provider Model:
+     Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+    <string name="mobile_data_no_connection">No connection</string>
+    <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
+    <string name="non_carrier_network_unavailable">No other networks available</string>
+    <!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
+    <string name="all_network_unavailable">No networks available</string>
+    <!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
+    <string name="turn_on_wifi">Wi\u2011Fi</string>
+    <!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
+    <string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
+    <!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
+    <string name="tap_a_network_to_connect">Tap a network to connect</string>
+    <!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
+    <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
+    <!-- Provider Model: Failure notification for connect -->
+    <string name="wifi_failed_connect_message">Failed to connect to network</string>
+    <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
+    <string name="see_all_networks">See all</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 70ed817..91d5d79 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -899,4 +899,43 @@
       <item name="android:colorBackground">@android:color/system_neutral1_900</item>
       <item name="android:itemBackground">@android:color/system_neutral1_800</item>
     </style>
+
+    <style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
+    </style>
+
+    <style name="Widget.SliceView.Panel">
+        <item name="titleSize">16sp</item>
+        <item name="rowStyle">@style/SliceRow</item>
+        <item name="android:background">?android:attr/colorBackgroundFloating</item>
+    </style>
+
+    <style name="SliceRow">
+        <!-- 2dp start padding for the start icon -->
+        <item name="titleItemStartPadding">2dp</item>
+        <item name="titleItemEndPadding">0dp</item>
+
+        <!-- Padding between content and the start icon is 14dp -->
+        <item name="contentStartPadding">14dp</item>
+        <!-- Padding between content and end items is 16dp -->
+        <item name="contentEndPadding">16dp</item>
+
+        <!-- Both side margins of end item are 16dp -->
+        <item name="endItemStartPadding">16dp</item>
+        <item name="endItemEndPadding">16dp</item>
+
+        <!-- Both side margins of bottom divider are 12dp -->
+        <item name="bottomDividerStartPadding">12dp</item>
+        <item name="bottomDividerEndPadding">12dp</item>
+
+        <item name="actionDividerHeight">32dp</item>
+    </style>
+
+    <style name="Theme.SystemUI.Dialog.Internet">
+        <item name="android:windowBackground">@drawable/internet_dialog_background</item>
+    </style>
+
+    <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index b2ae2a0..3a23094 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -47,6 +47,7 @@
 
     static_libs: [
         "PluginCoreLib",
+        "androidx.dynamicanimation_dynamicanimation",
     ],
     java_version: "1.8",
     min_sdk_version: "26",
diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml
index ec9d8fd..4bd6729 100644
--- a/packages/SystemUI/shared/lint-baseline.xml
+++ b/packages/SystemUI/shared/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
 
     <issue
         id="NewApi"
@@ -300,6 +300,17 @@
 
     <issue
         id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+        errorLine1="        taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;"
+        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+            line="192"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`"
         errorLine1="            isNotInRecents = !change.getTaskInfo().isRunning;"
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -311,6 +322,17 @@
 
     <issue
         id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`"
+        errorLine1="            isNotInRecents = !change.getTaskInfo().isRunning;"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+            line="210"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`"
         errorLine1="        leash.mSurfaceControl.release();"
         errorLine2="                              ~~~~~~~">
@@ -575,6 +597,17 @@
 
     <issue
         id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+        errorLine1="        onTaskMovedToFront(taskInfo.taskId);"
+        errorLine2="                           ~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java"
+            line="70"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
         message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`"
         errorLine1="                thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());"
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 277b2e3..de9558e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -77,8 +77,17 @@
     void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17;
 
     /**
-     * Sent IME status changes
+     * Sent when suggested rotation button could be shown
      */
-    void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition,
-                         boolean showImeSwitcher) = 18;
+    void onRotationProposal(int rotation, boolean isValid) = 18;
+
+    /**
+     * Sent when disable flags change
+     */
+    void disable(int displayId, int state1, int state2, boolean animate) = 19;
+
+    /**
+     * Sent when behavior changes. See WindowInsetsController#@Behavior
+     */
+    void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
 }
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 f72245b..11557ad 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
@@ -140,5 +140,8 @@
     /** Notifies that a swipe-up gesture has started */
     oneway void notifySwipeUpGestureStarted() = 46;
 
-    // Next id = 47
+    /** Notifies when taskbar status updated */
+    oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
+
+    // Next id = 48
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7dffc26..e33985d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -16,13 +16,24 @@
 
 package com.android.systemui.shared.recents.utilities;
 
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
 import android.os.Handler;
 import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.Surface;
 
 /* Common code */
 public class Utilities {
 
+    private static final float TABLET_MIN_DPS = 600;
+
     /**
      * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
      */
@@ -31,6 +42,23 @@
         h.sendMessageAtFrontOfQueue(msg);
     }
 
+    public static boolean isRotationAnimationCCW(int from, int to) {
+        // All 180deg WM rotation animations are CCW, match that
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+        return false; // Default
+    }
+
     /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
     public static float computeContrastBetweenColors(int bg, int fg) {
         float bgR = Color.red(bg) / 255f;
@@ -58,4 +86,52 @@
     public static float clamp(float value, float min, float max) {
         return Math.max(min, Math.min(max, value));
     }
+
+    /**
+     * @return updated set of flags from InputMethodService based off {@param oldHints}
+     *          Leaves original hints unmodified
+     */
+    public static int calculateBackDispositionHints(int oldHints, int backDisposition,
+            boolean imeShown, boolean showImeSwitcher) {
+        int hints = oldHints;
+        switch (backDisposition) {
+            case InputMethodService.BACK_DISPOSITION_DEFAULT:
+            case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+            case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+                if (imeShown) {
+                    hints |= NAVIGATION_HINT_BACK_ALT;
+                } else {
+                    hints &= ~NAVIGATION_HINT_BACK_ALT;
+                }
+                break;
+            case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
+                hints &= ~NAVIGATION_HINT_BACK_ALT;
+                break;
+        }
+        if (showImeSwitcher) {
+            hints |= NAVIGATION_HINT_IME_SHOWN;
+        } else {
+            hints &= ~NAVIGATION_HINT_IME_SHOWN;
+        }
+
+        return hints;
+    }
+
+    /** See {@link #isTablet(Configuration, Context)} */
+    public static boolean isTablet(Context context) {
+        Configuration newConfig = context.getResources().getConfiguration();
+        return isTablet(newConfig, context);
+    }
+
+    /**
+     * @return whether or not {@param newConfig} represents that of a large screen device or not
+     */
+    public static boolean isTablet(Configuration newConfig, Context context) {
+        float density = Resources.getSystem().getDisplayMetrics().density;
+        int size = Math.min((int) (density * newConfig.screenWidthDp),
+                (int) (density* newConfig.screenHeightDp));
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (size / densityRatio) >= TABLET_MIN_DPS;
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
new file mode 100644
index 0000000..5581a1c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.recents.utilities;
+
+import android.view.View;
+
+/**
+ * Shows view ripples by toggling the provided Views "pressed" state.
+ * Ripples 4 times.
+ */
+public class ViewRippler {
+    private static final int RIPPLE_OFFSET_MS = 50;
+    private static final int RIPPLE_INTERVAL_MS = 2000;
+    private View mRoot;
+
+    public void start(View root) {
+        stop(); // Stop any pending ripple animations
+
+        mRoot = root;
+
+        // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+        mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+        mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+        mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
+        mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
+        mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
+    }
+
+    public void stop() {
+        if (mRoot != null) mRoot.removeCallbacks(mRipple);
+    }
+
+    private final Runnable mRipple = new Runnable() {
+        @Override
+        public void run() { // Cause the ripple to fire via false presses
+            if (!mRoot.isAttachedToWindow()) return;
+            mRoot.setPressed(true /* pressed */);
+            mRoot.setPressed(false /* pressed */);
+        }
+    };
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c468e41..4663a9a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,6 +112,10 @@
     public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
     // The window magnification is overlapped with system gesture insets at the bottom.
     public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+    // ImeSwitcher is showing
+    public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
+    // Device dozing/AOD state
+    public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -133,7 +137,9 @@
             SYSUI_STATE_ONE_HANDED_ACTIVE,
             SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
             SYSUI_STATE_IME_SHOWING,
-            SYSUI_STATE_MAGNIFICATION_OVERLAP
+            SYSUI_STATE_MAGNIFICATION_OVERLAP,
+            SYSUI_STATE_IME_SWITCHER_SHOWING,
+            SYSUI_STATE_DEVICE_DOZING
     })
     public @interface SystemUiStateFlags {}
 
@@ -162,6 +168,8 @@
                 ? "allow_gesture" : "");
         str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
         str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
+        str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : "");
+        str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index ee55bf0..025d7ef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -69,7 +69,8 @@
         return mRemoteTransition;
     }
 
-    private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+    /** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */
+    public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
             final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
         return new IRemoteAnimationRunner.Stub() {
             @Override
@@ -260,7 +261,7 @@
                                 t.remove(leashMap.valueAt(i));
                             }
                             t.apply();
-                            finishCallback.onTransitionFinished(null /* wct */);
+                            finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
                         } catch (RemoteException e) {
                             Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
                                     + " finished callback", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 653d730..7f2d252 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -17,14 +17,19 @@
 package com.android.systemui.shared.system;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.Parcelable;
@@ -73,7 +78,7 @@
                     IRemoteTransitionFinishedCallback finishedCallback) {
                 final Runnable finishAdapter = () ->  {
                     try {
-                        finishedCallback.onTransitionFinished(null /* wct */);
+                        finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Failed to call transition finished callback", e);
                     }
@@ -87,7 +92,7 @@
                     IRemoteTransitionFinishedCallback finishedCallback) {
                 final Runnable finishAdapter = () ->  {
                     try {
-                        finishedCallback.onTransitionFinished(null /* wct */);
+                        finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Failed to call transition finished callback", e);
                     }
@@ -119,14 +124,20 @@
                 // This transition is for opening recents, so recents is on-top. We want to draw
                 // the current going-away task on top of recents, though, so move it to front
                 WindowContainerToken pausingTask = null;
+                WindowContainerToken pipTask = null;
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                     final TransitionInfo.Change change = info.getChanges().get(i);
                     if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
                         t.setLayer(leashMap.get(change.getLeash()),
                                 info.getChanges().size() * 3 - i);
-                        if (change.getTaskInfo() != null) {
+                        final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+                        if (taskInfo != null) {
                             pausingTask = change.getTaskInfo().token;
                         }
+                        if (taskInfo.pictureInPictureParams != null
+                                && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+                            pipTask = change.getTaskInfo().token;
+                        }
                     }
                 }
                 // Also make all the wallpapers opaque since we want the visible from the start
@@ -134,8 +145,8 @@
                     t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
                 }
                 t.apply();
-                mRecentsSession.setup(controller, info, finishedCallback, pausingTask,
-                        leashMap);
+                mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask,
+                        leashMap, mToken);
                 recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
                         new Rect());
             }
@@ -147,7 +158,7 @@
                 if (!mergeTarget.equals(mToken)) return;
                 if (!mRecentsSession.merge(info, t, recents)) return;
                 try {
-                    finishedCallback.onTransitionFinished(null /* wct */);
+                    finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error merging transition.", e);
                 }
@@ -156,14 +167,19 @@
     }
 
     /** Adds a filter check that restricts this remote transition to home open transitions. */
-    public void addHomeOpenCheck() {
+    public void addHomeOpenCheck(ComponentName homeActivity) {
         if (mFilter == null) {
             mFilter = new TransitionFilter();
         }
         mFilter.mRequirements =
-                new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+                new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+                        new TransitionFilter.Requirement()};
         mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+        mFilter.mRequirements[0].mTopActivity = homeActivity;
         mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+        mFilter.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+        mFilter.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+        mFilter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
     }
 
     /**
@@ -175,13 +191,17 @@
         private RecentsAnimationControllerCompat mWrapped = null;
         private IRemoteTransitionFinishedCallback mFinishCB = null;
         private WindowContainerToken mPausingTask = null;
+        private WindowContainerToken mPipTask = null;
         private TransitionInfo mInfo = null;
         private SurfaceControl mOpeningLeash = null;
         private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
+        private PictureInPictureSurfaceTransaction mPipTransaction = null;
+        private IBinder mTransition = null;
 
         void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
                 IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
-                ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+                WindowContainerToken pipTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
+                IBinder transition) {
             if (mInfo != null) {
                 throw new IllegalStateException("Trying to run a new recents animation while"
                         + " recents is already active.");
@@ -190,7 +210,9 @@
             mInfo = info;
             mFinishCB = finishCB;
             mPausingTask = pausingTask;
+            mPipTask = pipTask;
             mLeashMap = leashMap;
+            mTransition = transition;
         }
 
         @SuppressLint("NewApi")
@@ -247,6 +269,7 @@
 
         @Override public void setFinishTaskTransaction(int taskId,
                 PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+            mPipTransaction = finishTransaction;
             if (mWrapped != null) {
                 mWrapped.setFinishTaskTransaction(taskId, finishTransaction, overlay);
             }
@@ -263,10 +286,13 @@
             try {
                 if (!toHome && mPausingTask != null && mOpeningLeash == null) {
                     // The gesture went back to opening the app rather than continuing with
-                    // recents, so end the transition by moving the app back to the top.
+                    // recents, so end the transition by moving the app back to the top (and also
+                    // re-showing it's task).
                     final WindowContainerTransaction wct = new WindowContainerTransaction();
                     wct.reorder(mPausingTask, true /* onTop */);
-                    mFinishCB.onTransitionFinished(wct);
+                    final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                    t.show(mInfo.getChange(mPausingTask).getLeash());
+                    mFinishCB.onTransitionFinished(wct, t);
                 } else {
                     if (mOpeningLeash != null) {
                         // TODO: the launcher animation should handle this
@@ -275,7 +301,18 @@
                         t.setAlpha(mOpeningLeash, 1.f);
                         t.apply();
                     }
-                    mFinishCB.onTransitionFinished(null /* wct */);
+                    if (mPipTask != null && mPipTransaction != null) {
+                        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                        t.show(mInfo.getChange(mPipTask).getLeash());
+                        PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+                                mInfo.getChange(mPipTask).getLeash(), t);
+                        mPipTask = null;
+                        mPipTransaction = null;
+                        mFinishCB.onTransitionFinished(null /* wct */, t);
+                    } else {
+                        mFinishCB.onTransitionFinished(null /* wct */, null /* sct */);
+                    }
+
                 }
             } catch (RemoteException e) {
                 Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
@@ -298,6 +335,7 @@
             mInfo = null;
             mOpeningLeash = null;
             mLeashMap = null;
+            mTransition = null;
         }
 
         @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
@@ -318,6 +356,23 @@
         @Override public boolean removeTask(int taskId) {
             return mWrapped != null ? mWrapped.removeTask(taskId) : false;
         }
+
+        /**
+         * @see IRecentsAnimationController#detachNavigationBarFromApp
+         */
+        @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+            try {
+                ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to detach the navigation bar from app", e);
+            }
+        }
+
+        /**
+         * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+         */
+        @Override public void animateNavigationBarToApp(long duration) {
+        }
     }
 
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
index 2d01d6a..e49d9f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
@@ -16,6 +16,4 @@
 
 package com.android.systemui.shared.system.smartspace;
 
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-
-parcelable SmartspaceState;
\ No newline at end of file
+parcelable SmartspaceState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
rename to packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt
new file mode 100644
index 0000000..7594f50
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("UnfoldTransitionFactory")
+
+package com.android.unfold
+
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.unfold.updates.screen.ScreenStatusProvider
+import com.android.unfold.config.ANIMATION_MODE_HINGE_ANGLE
+import com.android.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.unfold.config.UnfoldTransitionConfig
+import com.android.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.unfold.updates.DeviceFoldStateProvider
+import com.android.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.unfold.updates.hinge.RotationSensorHingeAngleProvider
+import java.lang.IllegalStateException
+import java.util.concurrent.Executor
+
+fun createUnfoldTransitionProgressProvider(
+    context: Context,
+    config: UnfoldTransitionConfig,
+    screenStatusProvider: ScreenStatusProvider,
+    deviceStateManager: DeviceStateManager,
+    sensorManager: SensorManager,
+    mainHandler: Handler,
+    mainExecutor: Executor
+): UnfoldTransitionProgressProvider {
+
+    if (!config.isEnabled) {
+        throw IllegalStateException("Trying to create " +
+            "UnfoldTransitionProgressProvider when the transition is disabled")
+    }
+
+    val hingeAngleProvider =
+        if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+            RotationSensorHingeAngleProvider(sensorManager)
+        } else {
+            EmptyHingeAngleProvider()
+        }
+
+    val foldStateProvider = DeviceFoldStateProvider(
+        context,
+        hingeAngleProvider,
+        screenStatusProvider,
+        deviceStateManager,
+        mainExecutor
+    )
+
+    return if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+        PhysicsBasedUnfoldTransitionProgressProvider(
+            mainHandler,
+            foldStateProvider
+        )
+    } else {
+        FixedTimingTransitionProgressProvider(foldStateProvider)
+    }
+}
+
+fun createConfig(context: Context): UnfoldTransitionConfig =
+    ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt
new file mode 100644
index 0000000..2ddb49c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold
+
+import android.annotation.FloatRange
+import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.statusbar.policy.CallbackController
+
+/**
+ * Interface that allows to receive unfold transition progress updates.
+ * It can be used to update view properties based on the current animation progress.
+ * onTransitionProgress callback could be called on each frame.
+ *
+ * Use [createUnfoldTransitionProgressProvider] to create instances of this interface
+ */
+interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgressListener> {
+
+    fun destroy()
+
+    interface TransitionProgressListener {
+        fun onTransitionStarted()
+        fun onTransitionFinished()
+        fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float)
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt
new file mode 100644
index 0000000..bde87a5
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.config
+
+import android.content.Context
+import android.os.SystemProperties
+
+internal class ResourceUnfoldTransitionConfig(
+    private val context: Context
+) : UnfoldTransitionConfig {
+
+    override val isEnabled: Boolean
+        get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
+
+    @AnimationMode
+    override val mode: Int
+        get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
+            ANIMATION_MODE_FIXED_TIMING)
+
+    private fun readIsEnabled(): Boolean = context.resources
+        .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+}
+
+/**
+ * Temporary persistent property to control unfold transition mode
+ * See [com.android.unfold.config.AnimationMode]
+ */
+private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_mode"
diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt
new file mode 100644
index 0000000..f000c69
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.config
+
+import android.annotation.IntDef
+
+interface UnfoldTransitionConfig {
+    val isEnabled: Boolean
+
+    @AnimationMode
+    val mode: Int
+}
+
+@IntDef(prefix = ["ANIMATION_MODE_"], value = [
+    ANIMATION_MODE_DISABLED,
+    ANIMATION_MODE_FIXED_TIMING,
+    ANIMATION_MODE_HINGE_ANGLE
+])
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class AnimationMode
+
+const val ANIMATION_MODE_DISABLED = 0
+const val ANIMATION_MODE_FIXED_TIMING = 1
+const val ANIMATION_MODE_HINGE_ANGLE = 2
diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt
new file mode 100644
index 0000000..acfe073
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.progress
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.util.FloatProperty
+import com.android.unfold.UnfoldTransitionProgressProvider
+import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.unfold.updates.FoldStateProvider
+import com.android.unfold.updates.FoldStateProvider.FoldUpdate
+
+/**
+ * Emits animation progress with fixed timing after unfolding
+ */
+internal class FixedTimingTransitionProgressProvider(
+    private val foldStateProvider: FoldStateProvider
+) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
+
+    private val animatorListener = AnimatorListener()
+    private val animator =
+        ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f)
+            .apply {
+                duration = TRANSITION_TIME_MILLIS
+                addListener(animatorListener)
+            }
+
+
+    private var transitionProgress: Float = 0.0f
+        set(value) {
+            listeners.forEach { it.onTransitionProgress(value) }
+            field = value
+        }
+
+    private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+
+    init {
+        foldStateProvider.addCallback(this)
+        foldStateProvider.start()
+    }
+
+    override fun destroy() {
+        animator.cancel()
+        foldStateProvider.removeCallback(this)
+        foldStateProvider.stop()
+    }
+
+    override fun onFoldUpdate(@FoldUpdate update: Int) {
+        when (update) {
+            FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE ->
+                animator.start()
+            FOLD_UPDATE_FINISH_CLOSED ->
+                animator.cancel()
+        }
+    }
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        listeners.add(listener)
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        listeners.remove(listener)
+    }
+
+    override fun onHingeAngleUpdate(angle: Float) {
+    }
+
+    private object AnimationProgressProperty :
+        FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") {
+
+        override fun setValue(
+            provider: FixedTimingTransitionProgressProvider,
+            value: Float
+        ) {
+            provider.transitionProgress = value
+        }
+
+        override fun get(provider: FixedTimingTransitionProgressProvider): Float =
+            provider.transitionProgress
+    }
+
+    private inner class AnimatorListener : Animator.AnimatorListener {
+
+        override fun onAnimationStart(animator: Animator) {
+            listeners.forEach { it.onTransitionStarted() }
+        }
+
+        override fun onAnimationEnd(animator: Animator) {
+            listeners.forEach { it.onTransitionFinished() }
+        }
+
+        override fun onAnimationRepeat(animator: Animator) {
+        }
+
+        override fun onAnimationCancel(animator: Animator) {
+        }
+    }
+
+    private companion object {
+        private const val TRANSITION_TIME_MILLIS = 400L
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
new file mode 100644
index 0000000..d9d037f
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.progress
+
+import android.os.Handler
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.unfold.UnfoldTransitionProgressProvider
+import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.unfold.updates.FoldStateProvider
+import com.android.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
+
+/**
+ * Maps fold updates to unfold transition progress using DynamicAnimation.
+ *
+ * TODO(b/193793338) Current limitations:
+ *  - doesn't handle folding transition
+ *  - doesn't handle postures
+ */
+internal class PhysicsBasedUnfoldTransitionProgressProvider(
+    private val handler: Handler,
+    private val foldStateProvider: FoldStateProvider
+) :
+    UnfoldTransitionProgressProvider,
+    FoldUpdatesListener,
+    DynamicAnimation.OnAnimationEndListener {
+
+    private val springAnimation = SpringAnimation(this, AnimationProgressProperty)
+        .apply {
+            addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
+        }
+
+    private val timeoutRunnable = TimeoutRunnable()
+
+    private var isTransitionRunning = false
+    private var isAnimatedCancelRunning = false
+
+    private var transitionProgress: Float = 0.0f
+        set(value) {
+            if (isTransitionRunning) {
+                listeners.forEach { it.onTransitionProgress(value) }
+            }
+            field = value
+        }
+
+    private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+
+    init {
+        foldStateProvider.addCallback(this)
+        foldStateProvider.start()
+    }
+
+    override fun destroy() {
+        foldStateProvider.stop()
+    }
+
+    override fun onHingeAngleUpdate(angle: Float) {
+        if (!isTransitionRunning || isAnimatedCancelRunning) return
+        springAnimation.animateToFinalPosition(angle / 180f)
+    }
+
+    override fun onFoldUpdate(@FoldUpdate update: Int) {
+        when (update) {
+            FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
+                onStartTransition()
+                startTransition(startValue = 0f)
+            }
+            FOLD_UPDATE_FINISH_FULL_OPEN -> {
+                cancelTransition(endValue = 1f, animate = true)
+            }
+            FOLD_UPDATE_FINISH_CLOSED -> {
+                cancelTransition(endValue = 0f, animate = false)
+            }
+        }
+    }
+
+    private fun cancelTransition(endValue: Float, animate: Boolean) {
+        handler.removeCallbacks(timeoutRunnable)
+
+        if (animate) {
+            isAnimatedCancelRunning = true
+            springAnimation.animateToFinalPosition(endValue)
+        } else {
+            transitionProgress = endValue
+            isAnimatedCancelRunning = false
+            isTransitionRunning = false
+            springAnimation.cancel()
+
+            listeners.forEach {
+                it.onTransitionFinished()
+            }
+        }
+    }
+
+    override fun onAnimationEnd(
+        animation: DynamicAnimation<out DynamicAnimation<*>>,
+        canceled: Boolean,
+        value: Float,
+        velocity: Float
+    ) {
+        if (isAnimatedCancelRunning) {
+            cancelTransition(value, animate = false)
+        }
+    }
+
+    private fun onStartTransition() {
+        listeners.forEach {
+            it.onTransitionStarted()
+        }
+        isTransitionRunning = true
+    }
+
+    private fun startTransition(startValue: Float) {
+        if (!isTransitionRunning) onStartTransition()
+
+        springAnimation.apply {
+            spring = SpringForce().apply {
+                finalPosition = startValue
+                dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+                stiffness = SPRING_STIFFNESS
+            }
+            minimumVisibleChange = MINIMAL_VISIBLE_CHANGE
+            setStartValue(startValue)
+            setMinValue(0f)
+            setMaxValue(1f)
+        }
+
+        springAnimation.start()
+
+        handler.postDelayed(timeoutRunnable, TRANSITION_TIMEOUT_MILLIS)
+    }
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        listeners.add(listener)
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        listeners.remove(listener)
+    }
+
+    private inner class TimeoutRunnable : Runnable {
+
+        override fun run() {
+            cancelTransition(endValue = 1f, animate = true)
+        }
+    }
+
+    private object AnimationProgressProperty :
+        FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
+
+        override fun setValue(
+            provider: PhysicsBasedUnfoldTransitionProgressProvider,
+            value: Float
+        ) {
+            provider.transitionProgress = value
+        }
+
+        override fun getValue(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
+            provider.transitionProgress
+    }
+}
+
+private const val TRANSITION_TIMEOUT_MILLIS = 2000L
+private const val SPRING_STIFFNESS = 200.0f
+private const val MINIMAL_VISIBLE_CHANGE = 0.001f
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt
new file mode 100644
index 0000000..3a21b80
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.updates
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import androidx.core.util.Consumer
+import com.android.unfold.updates.screen.ScreenStatusProvider
+import com.android.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.unfold.updates.hinge.FULLY_OPEN_DEGREES
+import com.android.unfold.updates.hinge.HingeAngleProvider
+import java.util.concurrent.Executor
+
+internal class DeviceFoldStateProvider(
+    context: Context,
+    private val hingeAngleProvider: HingeAngleProvider,
+    private val screenStatusProvider: ScreenStatusProvider,
+    private val deviceStateManager: DeviceStateManager,
+    private val mainExecutor: Executor
+) : FoldStateProvider {
+
+    private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
+
+    @FoldUpdate
+    private var lastFoldUpdate: Int? = null
+
+    private val hingeAngleListener = HingeAngleListener()
+    private val screenListener = ScreenStatusListener()
+    private val foldStateListener = FoldStateListener(context)
+
+    private var isFolded = false
+
+    override fun start() {
+        deviceStateManager.registerCallback(
+            mainExecutor,
+            foldStateListener
+        )
+        screenStatusProvider.addCallback(screenListener)
+        hingeAngleProvider.addCallback(hingeAngleListener)
+    }
+
+    override fun stop() {
+        screenStatusProvider.removeCallback(screenListener)
+        deviceStateManager.unregisterCallback(foldStateListener)
+        hingeAngleProvider.removeCallback(hingeAngleListener)
+        hingeAngleProvider.stop()
+    }
+
+    override fun addCallback(listener: FoldUpdatesListener) {
+        outputListeners.add(listener)
+    }
+
+    override fun removeCallback(listener: FoldUpdatesListener) {
+        outputListeners.remove(listener)
+    }
+
+    private fun onHingeAngle(angle: Float) {
+        when (lastFoldUpdate) {
+            FOLD_UPDATE_FINISH_FULL_OPEN -> {
+                if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
+                    lastFoldUpdate = FOLD_UPDATE_START_CLOSING
+                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
+                }
+            }
+            FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
+                if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
+                    lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
+                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
+                }
+            }
+        }
+
+        outputListeners.forEach { it.onHingeAngleUpdate(angle) }
+    }
+
+    private inner class FoldStateListener(context: Context) :
+        DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
+            isFolded = folded
+
+            if (folded) {
+                lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
+                outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
+                hingeAngleProvider.stop()
+            } else {
+                lastFoldUpdate = FOLD_UPDATE_START_OPENING
+                outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
+                hingeAngleProvider.start()
+            }
+        })
+
+    private inner class ScreenStatusListener :
+        ScreenStatusProvider.ScreenListener {
+
+        override fun onScreenTurnedOn() {
+            if (!isFolded) {
+                outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }
+            }
+        }
+    }
+
+    private inner class HingeAngleListener : Consumer<Float> {
+
+        override fun accept(angle: Float) {
+            onHingeAngle(angle)
+        }
+    }
+}
+
+private const val MOVEMENT_THRESHOLD_DEGREES = 10f
+private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt
new file mode 100644
index 0000000..2c3a6ec
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.updates
+
+import android.annotation.FloatRange
+import android.annotation.IntDef
+import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.statusbar.policy.CallbackController
+
+/**
+ * Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
+ * start folding/unfolding, screen availability
+ */
+internal interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
+    fun start()
+    fun stop()
+
+    interface FoldUpdatesListener {
+        fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float)
+        fun onFoldUpdate(@FoldUpdate update: Int)
+    }
+
+    @IntDef(prefix = ["FOLD_UPDATE_"], value = [
+        FOLD_UPDATE_START_OPENING,
+        FOLD_UPDATE_HALF_OPEN,
+        FOLD_UPDATE_START_CLOSING,
+        FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
+        FOLD_UPDATE_FINISH_HALF_OPEN,
+        FOLD_UPDATE_FINISH_FULL_OPEN,
+        FOLD_UPDATE_FINISH_CLOSED
+    ])
+    @Retention(AnnotationRetention.SOURCE)
+    annotation class FoldUpdate
+}
+
+const val FOLD_UPDATE_START_OPENING = 0
+const val FOLD_UPDATE_HALF_OPEN = 1
+const val FOLD_UPDATE_START_CLOSING = 2
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 3
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 4
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 5
+const val FOLD_UPDATE_FINISH_CLOSED = 6
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt
new file mode 100644
index 0000000..905b086
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -0,0 +1,17 @@
+package com.android.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+
+internal class EmptyHingeAngleProvider : HingeAngleProvider {
+    override fun start() {
+    }
+
+    override fun stop() {
+    }
+
+    override fun removeCallback(listener: Consumer<Float>) {
+    }
+
+    override fun addCallback(listener: Consumer<Float>) {
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt
new file mode 100644
index 0000000..4196f60
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt
@@ -0,0 +1,12 @@
+package com.android.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+import com.android.systemui.statusbar.policy.CallbackController
+
+internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
+    fun start()
+    fun stop()
+}
+
+const val FULLY_OPEN_DEGREES = 180f
+const val FULLY_CLOSED_DEGREES = 0f
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
new file mode 100644
index 0000000..011582e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
@@ -0,0 +1,67 @@
+package com.android.unfold.updates.hinge
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import androidx.core.util.Consumer
+import com.android.systemui.shared.recents.utilities.Utilities
+
+/**
+ * Temporary hinge angle provider that uses rotation sensor instead.
+ * It requires to have the device in a certain position to work correctly
+ * (flat to the ground)
+ */
+internal class RotationSensorHingeAngleProvider(
+    private val sensorManager: SensorManager
+) : HingeAngleProvider {
+
+    private val sensorListener = HingeAngleSensorListener()
+    private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+
+    override fun start() {
+        val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
+        sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
+    }
+
+    override fun stop() {
+        sensorManager.unregisterListener(sensorListener)
+    }
+
+    override fun removeCallback(listener: Consumer<Float>) {
+        listeners.remove(listener)
+    }
+
+    override fun addCallback(listener: Consumer<Float>) {
+        listeners.add(listener)
+    }
+
+    private fun onHingeAngle(angle: Float) {
+        listeners.forEach { it.accept(angle) }
+    }
+
+    private inner class HingeAngleSensorListener : SensorEventListener {
+
+        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+        }
+
+        override fun onSensorChanged(event: SensorEvent) {
+            // Jumbojack sends incorrect sensor reading 1.0f event in the beginning, let's ignore it
+            if (event.values[3] == 1.0f) return
+
+            val angleRadians = event.values.convertToAngle()
+            val hingeAngleDegrees = Math.toDegrees(angleRadians).toFloat()
+            val angle = Utilities.clamp(hingeAngleDegrees, FULLY_CLOSED_DEGREES, FULLY_OPEN_DEGREES)
+            onHingeAngle(angle)
+        }
+
+        private val rotationMatrix = FloatArray(9)
+        private val resultOrientation = FloatArray(9)
+
+        private fun FloatArray.convertToAngle(): Double {
+            SensorManager.getRotationMatrixFromVector(rotationMatrix, this)
+            SensorManager.getOrientation(rotationMatrix, resultOrientation)
+            return resultOrientation[2] + Math.PI
+        }
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt
new file mode 100644
index 0000000..a65e888
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.updates.screen
+
+import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.statusbar.policy.CallbackController
+
+interface ScreenStatusProvider : CallbackController<ScreenListener> {
+
+    interface ScreenListener {
+        /**
+         * Called when the screen is on and ready (windows are drawn and screen blocker is removed)
+         */
+        fun onScreenTurnedOn()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a5b2509..8f14cd8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,6 +14,9 @@
 import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.dagger.KeyguardStatusViewScope;
 import com.android.systemui.R;
@@ -22,6 +25,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.TimeZone;
 
@@ -37,6 +42,13 @@
     private static final long CLOCK_IN_MILLIS = 200;
     private static final long SMARTSPACE_MOVE_MILLIS = 350;
 
+    @IntDef({LARGE, SMALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClockSize { }
+
+    public static final int LARGE = 0;
+    public static final int SMALL = 1;
+
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -65,13 +77,13 @@
     private float mDarkAmount;
 
     /**
-     * Boolean value indicating if notifications are visible on lock screen. Use null to signify
-     * it is uninitialized.
+     * Indicates which clock is currently displayed - should be one of {@link ClockSize}.
+     * Use null to signify it is uninitialized.
      */
-    private Boolean mHasVisibleNotifications = null;
+    @ClockSize private Integer mDisplayedClockSize = null;
 
-    private AnimatorSet mClockInAnim = null;
-    private AnimatorSet mClockOutAnim = null;
+    @VisibleForTesting AnimatorSet mClockInAnim = null;
+    @VisibleForTesting AnimatorSet mClockOutAnim = null;
     private ObjectAnimator mSmartspaceAnim = null;
 
     /**
@@ -264,19 +276,17 @@
     }
 
     /**
-     * Based upon whether notifications are showing or not, display/hide the large clock and
-     * the smaller version.
+     * Display the desired clock and hide the other one
+     *
+     * @return true if desired clock appeared and false if it was already visible
      */
-    boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
-        if (mHasVisibleNotifications != null
-                && hasVisibleNotifications == mHasVisibleNotifications) {
+    boolean switchToClock(@ClockSize int clockSize) {
+        if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
             return false;
         }
-        boolean useLargeClock = !hasVisibleNotifications;
-        animateClockChange(useLargeClock);
-
-        mHasVisibleNotifications = hasVisibleNotifications;
-        return useLargeClock;
+        animateClockChange(clockSize == LARGE);
+        mDisplayedClockSize = clockSize;
+        return true;
     }
 
     public Paint getPaint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 632919a..9e456cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,6 +19,8 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+
 import android.app.WallpaperManager;
 import android.text.TextUtils;
 import android.view.View;
@@ -245,10 +247,12 @@
     }
 
     /**
-     * Set whether or not the lock screen is showing notifications.
+     * Set which clock should be displayed on the keyguard. The other one will be automatically
+     * hidden.
      */
-    public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
-        if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
+    public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+        boolean appeared = mView.switchToClock(clockSize);
+        if (appeared && clockSize == LARGE) {
             mLargeClockViewController.animateAppear();
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ca4d73b..840e8c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -42,7 +42,6 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.SpringAnimation;
@@ -104,7 +103,6 @@
 
     private boolean mIsSecurityViewLeftAligned = true;
     private boolean mOneHandedMode = false;
-    private SecurityMode mSecurityMode = SecurityMode.Invalid;
     private ViewPropertyAnimator mRunningOneHandedAnimator;
 
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -248,66 +246,47 @@
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
-        mSecurityMode = securityMode;
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
         updateBiometricRetry(securityMode, faceAuthEnabled);
-
-        updateLayoutForSecurityMode(securityMode);
     }
 
-    void updateLayoutForSecurityMode(SecurityMode securityMode) {
-        mSecurityMode = securityMode;
-        mOneHandedMode = canUseOneHandedBouncer();
-
-        if (mOneHandedMode) {
-            mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
-        }
-
+    /**
+     * Sets whether this security container is in one handed mode. If so, it will measure its
+     * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite
+     * side of the screen.
+     */
+    public void setOneHandedMode(boolean oneHandedMode) {
+        mOneHandedMode = oneHandedMode;
         updateSecurityViewGravity();
         updateSecurityViewLocation(false);
     }
 
-    /** Update keyguard position based on a tapped X coordinate. */
-    public void updateKeyguardPosition(float x) {
-        if (mOneHandedMode) {
-            moveBouncerForXCoordinate(x, /* animate= */false);
-        }
+    /** Returns whether this security container is in one-handed mode. */
+    public boolean isOneHandedMode() {
+        return mOneHandedMode;
     }
 
-    /** Return whether the one-handed keyguard should be enabled. */
-    private boolean canUseOneHandedBouncer() {
-        // Is it enabled?
-        if (!getResources().getBoolean(
-                com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
-            return false;
-        }
-
-        if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
-            return false;
-        }
-
-        return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+    /**
+     * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the
+     * left-hand side of the screen or not, and whether to animate when moving between the two.
+     */
+    public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) {
+        mIsSecurityViewLeftAligned = leftAligned;
+        updateSecurityViewLocation(animate);
     }
 
-    /** Read whether the one-handed keyguard should be on the left/right from settings. */
-    private boolean isOneHandedKeyguardLeftAligned(Context context) {
-        try {
-            return Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
-                    == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
-        } catch (Settings.SettingNotFoundException ex) {
-            return true;
-        }
+    /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
+    public boolean isOneHandedModeLeftAligned() {
+        return mIsSecurityViewLeftAligned;
     }
 
     private void updateSecurityViewGravity() {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
+        if (mSecurityViewFlipper == null) {
             return;
         }
 
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+        FrameLayout.LayoutParams lp =
+                (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams();
 
         if (mOneHandedMode) {
             lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
@@ -315,7 +294,7 @@
             lp.gravity = Gravity.CENTER_HORIZONTAL;
         }
 
-        securityView.setLayoutParams(lp);
+        mSecurityViewFlipper.setLayoutParams(lp);
     }
 
     /**
@@ -324,14 +303,12 @@
      * by the security view .
      */
     private void updateSecurityViewLocation(boolean animate) {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
+        if (mSecurityViewFlipper == null) {
             return;
         }
 
         if (!mOneHandedMode) {
-            securityView.setTranslationX(0);
+            mSecurityViewFlipper.setTranslationX(0);
             return;
         }
 
@@ -343,7 +320,8 @@
         int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
 
         if (animate) {
-            mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+            mRunningOneHandedAnimator =
+                    mSecurityViewFlipper.animate().translationX(targetTranslation);
             mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
             mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
                 @Override
@@ -355,27 +333,10 @@
             mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
             mRunningOneHandedAnimator.start();
         } else {
-            securityView.setTranslationX(targetTranslation);
+            mSecurityViewFlipper.setTranslationX(targetTranslation);
         }
     }
 
-    @Nullable
-    private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-
-            if (isKeyguardSecurityView(child)) {
-                return (KeyguardSecurityViewFlipper) child;
-            }
-        }
-
-        return null;
-    }
-
-    private boolean isKeyguardSecurityView(View view) {
-        return view instanceof KeyguardSecurityViewFlipper;
-    }
-
     public void onPause() {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
@@ -635,7 +596,7 @@
         for (int i = 0; i < getChildCount(); i++) {
             final View view = getChildAt(i);
             if (view.getVisibility() != GONE) {
-                if (mOneHandedMode && isKeyguardSecurityView(view)) {
+                if (mOneHandedMode && view == mSecurityViewFlipper) {
                     measureChildWithMargins(view, halfWidthMeasureSpec, 0,
                             heightMeasureSpec, 0);
                 } else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fde8213..cb5c6c3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -32,6 +32,7 @@
 import android.content.res.Configuration;
 import android.metrics.LogMaker;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
@@ -49,6 +50,8 @@
 import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Gefingerpoken;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -74,12 +77,14 @@
     private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
     private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
+    private final FalsingCollector mFalsingCollector;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
     private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
 
-    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+    @VisibleForTesting
+    final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
         private MotionEvent mTouchDown;
         @Override
         public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -91,6 +96,17 @@
             // Do just a bit of our own falsing. People should only be tapping on the input, not
             // swiping.
             if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                // If we're in one handed mode, the user can tap on the opposite side of the screen
+                // to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
+                // to move the bouncer to each screen side can end up closing it instead).
+                if (mView.isOneHandedMode()) {
+                    if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f)
+                            || (!mView.isOneHandedModeLeftAligned()
+                            && ev.getX() <= mView.getWidth() / 2f)) {
+                        mFalsingCollector.avoidGesture();
+                    }
+                }
+
                 if (mTouchDown != null) {
                     mTouchDown.recycle();
                     mTouchDown = null;
@@ -202,7 +218,8 @@
             KeyguardStateController keyguardStateController,
             SecurityCallback securityCallback,
             KeyguardSecurityViewFlipperController securityViewFlipperController,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            FalsingCollector falsingCollector) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -216,6 +233,7 @@
                 mKeyguardSecurityCallback);
         mConfigurationController = configurationController;
         mLastOrientation = getResources().getConfiguration().orientation;
+        mFalsingCollector = falsingCollector;
     }
 
     @Override
@@ -440,13 +458,49 @@
         if (newView != null) {
             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
             mSecurityViewFlipperController.show(newView);
-            mView.updateLayoutForSecurityMode(securityMode);
+            configureOneHandedMode();
         }
 
         mSecurityCallback.onSecurityModeChanged(
                 securityMode, newView != null && newView.needsInput());
     }
 
+    /** Read whether the one-handed keyguard should be on the left/right from settings. */
+    private boolean isOneHandedKeyguardLeftAligned() {
+        try {
+            return Settings.Global.getInt(mView.getContext().getContentResolver(),
+                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+                    == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+        } catch (Settings.SettingNotFoundException ex) {
+            return true;
+        }
+    }
+
+    private boolean canUseOneHandedBouncer() {
+        // Is it enabled?
+        if (!getResources().getBoolean(
+                com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
+            return false;
+        }
+
+        if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) {
+            return false;
+        }
+
+        return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+    }
+
+    private void configureOneHandedMode() {
+        boolean oneHandedMode = canUseOneHandedBouncer();
+
+        mView.setOneHandedMode(oneHandedMode);
+
+        if (oneHandedMode) {
+            mView.setOneHandedModeLeftAligned(
+                    isOneHandedKeyguardLeftAligned(), /* animate= */false);
+        }
+    }
+
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
         // +1 for this time
         final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
@@ -511,13 +565,15 @@
         int newOrientation = getResources().getConfiguration().orientation;
         if (newOrientation != mLastOrientation) {
             mLastOrientation = newOrientation;
-            mView.updateLayoutForSecurityMode(mCurrentSecurityMode);
+            configureOneHandedMode();
         }
     }
 
     /** Update keyguard position based on a tapped X coordinate. */
     public void updateKeyguardPosition(float x) {
-        mView.updateKeyguardPosition(x);
+        if (mView.isOneHandedMode()) {
+            mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false);
+        }
     }
 
     static class Factory {
@@ -533,6 +589,7 @@
         private final KeyguardStateController mKeyguardStateController;
         private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
         private final ConfigurationController mConfigurationController;
+        private final FalsingCollector mFalsingCollector;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -545,7 +602,8 @@
                 UiEventLogger uiEventLogger,
                 KeyguardStateController keyguardStateController,
                 KeyguardSecurityViewFlipperController securityViewFlipperController,
-                ConfigurationController configurationController) {
+                ConfigurationController configurationController,
+                FalsingCollector falsingCollector) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
             mLockPatternUtils = lockPatternUtils;
@@ -556,6 +614,7 @@
             mKeyguardStateController = keyguardStateController;
             mSecurityViewFlipperController = securityViewFlipperController;
             mConfigurationController = configurationController;
+            mFalsingCollector = falsingCollector;
         }
 
         public KeyguardSecurityContainerController create(
@@ -564,7 +623,7 @@
                     mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController);
+                    mConfigurationController, mFalsingCollector);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 72e5028..8bf8e09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.util.Slog;
 
+import com.android.keyguard.KeyguardClockSwitch.ClockSize;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -116,10 +117,11 @@
     }
 
     /**
-     * Set whether or not the lock screen is showing notifications.
+     * Set which clock should be displayed on the keyguard. The other one will be automatically
+     * hidden.
      */
-    public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
-        mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+    public void displayClock(@ClockSize int clockSize) {
+        mKeyguardClockSwitchController.displayClock(clockSize);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 310a0d3..0e25fac 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2829,6 +2829,20 @@
         updateBiometricListeningState();
     }
 
+    /** Notifies that the occluded state changed. */
+    public void onKeyguardOccludedChanged(boolean occluded) {
+        Assert.isMainThread();
+        if (DEBUG) {
+            Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")");
+        }
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onKeyguardOccludedChanged(occluded);
+            }
+        }
+    }
+
     /**
      * Handle {@link #MSG_KEYGUARD_RESET}
      */
@@ -3011,6 +3025,7 @@
         callback.onPhoneStateChanged(mPhoneState);
         callback.onRefreshCarrierInfo();
         callback.onClockVisibilityChanged();
+        callback.onKeyguardOccludedChanged(mKeyguardOccluded);
         callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         callback.onTelephonyCapable(mTelephonyCapable);
         callback.onLockScreenModeChanged(mLockScreenMode);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 9849a7e..6aa7aaa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -88,6 +88,12 @@
      */
     public void onKeyguardVisibilityChanged(boolean showing) { }
 
+    /**
+     * Called when the keyguard occluded state changes.
+     * @param occluded Indicates if the keyguard is now occluded.
+     */
+    public void onKeyguardOccludedChanged(boolean occluded) { }
+
     public void onKeyguardVisibilityChangedRaw(boolean showing) {
         final long now = SystemClock.elapsedRealtime();
         if (showing == mShowing
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 62d5a45..06fbe84 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -39,113 +39,114 @@
 @SysUISingleton
 public class ActivityStarterDelegate implements ActivityStarter {
 
-    private Optional<Lazy<StatusBar>> mActualStarter;
+    private Lazy<Optional<StatusBar>> mActualStarterOptionalLazy;
 
     @Inject
-    public ActivityStarterDelegate(Optional<Lazy<StatusBar>> statusBar) {
-        mActualStarter = statusBar;
+    public ActivityStarterDelegate(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+        mActualStarterOptionalLazy = statusBarOptionalLazy;
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startPendingIntentDismissingKeyguard(intent));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startPendingIntentDismissingKeyguard(intent));
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentUiThreadCallback));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startPendingIntentDismissingKeyguard(
+                        intent, intentSentUiThreadCallback));
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback, View associatedView) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentUiThreadCallback, associatedView));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startPendingIntentDismissingKeyguard(
+                        intent, intentSentUiThreadCallback, associatedView));
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback,
             ActivityLaunchAnimator.Controller animationController) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentUiThreadCallback, animationController));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startPendingIntentDismissingKeyguard(
+                        intent, intentSentUiThreadCallback, animationController));
     }
 
     @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade,
-                        flags));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startActivity(intent, onlyProvisioned, dismissShade, flags));
     }
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade) {
-        mActualStarter.ifPresent(starter -> starter.get().startActivity(intent, dismissShade));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startActivity(intent, dismissShade));
     }
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade,
             @Nullable ActivityLaunchAnimator.Controller animationController) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startActivity(intent, dismissShade, animationController));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startActivity(intent, dismissShade, animationController));
     }
 
     @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startActivity(intent, onlyProvisioned, dismissShade));
     }
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().startActivity(intent, dismissShade, callback));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.startActivity(intent, dismissShade, callback));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(Intent intent, int delay) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.postStartActivityDismissingKeyguard(intent, delay));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(Intent intent, int delay,
             @Nullable ActivityLaunchAnimator.Controller animationController) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
-                        animationController));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.postStartActivityDismissingKeyguard(
+                        intent, delay, animationController));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(PendingIntent intent) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().postStartActivityDismissingKeyguard(intent));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.postStartActivityDismissingKeyguard(intent));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(PendingIntent intent,
             ActivityLaunchAnimator.Controller animationController) {
-        mActualStarter.ifPresent(starter ->
-                starter.get().postStartActivityDismissingKeyguard(intent, animationController));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.postStartActivityDismissingKeyguard(
+                        intent, animationController));
     }
 
     @Override
     public void postQSRunnableDismissingKeyguard(Runnable runnable) {
-        mActualStarter.ifPresent(
-                starter -> starter.get().postQSRunnableDismissingKeyguard(runnable));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.postQSRunnableDismissingKeyguard(runnable));
     }
 
     @Override
     public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancel,
             boolean afterKeyguardGone) {
-        mActualStarter.ifPresent(starter -> starter.get().dismissKeyguardThenExecute(action, cancel,
-                afterKeyguardGone));
+        mActualStarterOptionalLazy.get().ifPresent(
+                starter -> starter.dismissKeyguardThenExecute(action, cancel, afterKeyguardGone));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 54e78cf..6fc2e41 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -192,6 +192,7 @@
      * 0 - No preference
      * 1 - Force on
      * 2 - Force off
+     * 3 - Estimate
      * @param mode desired mode (none, on, off)
      */
     public void setPercentShowMode(@BatteryPercentMode int mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 76f30a8..d258eca 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -65,8 +65,8 @@
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
 import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.recents.Recents;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -99,7 +99,6 @@
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -350,8 +349,6 @@
     @Inject Lazy<DozeParameters> mDozeParameters;
     @Inject Lazy<IWallpaperManager> mWallpaperManager;
     @Inject Lazy<CommandQueue> mCommandQueue;
-    @Inject Lazy<Recents> mRecents;
-    @Inject Lazy<StatusBar> mStatusBar;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
     @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
@@ -362,8 +359,9 @@
     @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
     @Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy;
     @Inject Lazy<UiEventLogger> mUiEventLogger;
-    @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
     @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
+    @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
+    @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
 
     @Inject
     public Dependency() {
@@ -555,8 +553,6 @@
         mProviders.put(DozeParameters.class, mDozeParameters::get);
         mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
         mProviders.put(CommandQueue.class, mCommandQueue::get);
-        mProviders.put(Recents.class, mRecents::get);
-        mProviders.put(StatusBar.class, mStatusBar::get);
         mProviders.put(ProtoTracer.class, mProtoTracer::get);
         mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
         mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get);
@@ -576,6 +572,7 @@
         mProviders.put(SystemStatusAnimationScheduler.class,
                 mSystemStatusAnimationSchedulerLazy::get);
         mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+        mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
         mProviders.put(EdgeBackGestureHandler.Factory.class,
                 mEdgeBackGestureHandlerFactoryLazy::get);
         mProviders.put(UiEventLogger.class, mUiEventLogger::get);
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 0ae89bc..1d355f2 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -16,25 +16,24 @@
 
 package com.android.systemui;
 
-import android.app.ActivityManager;
-import android.app.Dialog;
+import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
-import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.settings.SecureSettings;
 
 /**
  * Manages notification when a guest session is resumed.
@@ -43,16 +42,23 @@
 
     private static final String TAG = "GuestResumeSessionReceiver";
 
-    private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
+    @VisibleForTesting
+    public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
 
-    private Dialog mNewSessionDialog;
+    @VisibleForTesting
+    public AlertDialog mNewSessionDialog;
+    private final UserTracker mUserTracker;
     private final UserSwitcherController mUserSwitcherController;
     private final UiEventLogger mUiEventLogger;
+    private final SecureSettings mSecureSettings;
 
     public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
-            UiEventLogger uiEventLogger) {
+            UserTracker userTracker, UiEventLogger uiEventLogger,
+            SecureSettings secureSettings) {
         mUserSwitcherController = userSwitcherController;
+        mUserTracker = userTracker;
         mUiEventLogger = uiEventLogger;
+        mSecureSettings = secureSettings;
     }
 
     /**
@@ -78,26 +84,19 @@
                 return;
             }
 
-            UserInfo currentUser;
-            try {
-                currentUser = ActivityManager.getService().getCurrentUser();
-            } catch (RemoteException e) {
-                return;
-            }
+            UserInfo currentUser = mUserTracker.getUserInfo();
             if (!currentUser.isGuest()) {
                 return;
             }
 
-            ContentResolver cr = context.getContentResolver();
-            int notFirstLogin = Settings.System.getIntForUser(
-                    cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
+            int notFirstLogin = mSecureSettings.getIntForUser(
+                    SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
             if (notFirstLogin != 0) {
                 mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
-                        mUiEventLogger, userId);
+                        mUserTracker, mUiEventLogger, userId);
                 mNewSessionDialog.show();
             } else {
-                Settings.System.putIntForUser(
-                        cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
+                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
             }
         }
     }
@@ -109,18 +108,27 @@
         }
     }
 
-    private static class ResetSessionDialog extends SystemUIDialog implements
+    /**
+     * Dialog shown when user when asking for confirmation before deleting guest user.
+     */
+    @VisibleForTesting
+    public static class ResetSessionDialog extends SystemUIDialog implements
             DialogInterface.OnClickListener {
 
-        private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
-        private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+        @VisibleForTesting
+        public static final int BUTTON_WIPE = BUTTON_NEGATIVE;
+        @VisibleForTesting
+        public static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
 
         private final UserSwitcherController mUserSwitcherController;
         private final UiEventLogger mUiEventLogger;
         private final int mUserId;
 
-        ResetSessionDialog(Context context, UserSwitcherController userSwitcherController,
-                UiEventLogger uiEventLogger, int userId) {
+        ResetSessionDialog(Context context,
+                UserSwitcherController userSwitcherController,
+                UserTracker userTracker,
+                UiEventLogger uiEventLogger,
+                int userId) {
             super(context);
 
             setTitle(context.getString(R.string.guest_wipe_session_title));
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a68f796..c8f8404 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,8 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
@@ -29,7 +31,6 @@
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Size;
-import android.view.DisplayInfo;
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
 
@@ -90,7 +91,7 @@
         mMiniBitmap = null;
     }
 
-    class GLEngine extends Engine {
+    class GLEngine extends Engine implements DisplayListener {
         // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
         // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
         @VisibleForTesting
@@ -102,15 +103,15 @@
         private EglHelper mEglHelper;
         private final Runnable mFinishRenderingTask = this::finishRendering;
         private boolean mNeedRedraw;
-        private int mWidth = 1;
-        private int mHeight = 1;
+
+        private boolean mDisplaySizeValid = false;
+        private int mDisplayWidth = 1;
+        private int mDisplayHeight = 1;
+
         private int mImgWidth = 1;
         private int mImgHeight = 1;
-        private float mPageWidth = 1.f;
-        private float mPageOffset = 1.f;
 
-        GLEngine() {
-        }
+        GLEngine() { }
 
         @VisibleForTesting
         GLEngine(Handler handler) {
@@ -124,13 +125,23 @@
             mRenderer = getRendererInstance();
             setFixedSizeAllowed(true);
             updateSurfaceSize();
-            Rect window = getDisplayContext()
-                    .getSystemService(WindowManager.class)
-                    .getCurrentWindowMetrics()
-                    .getBounds();
-            mHeight = window.height();
-            mWidth = window.width();
+
             mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
+            getDisplayContext().getSystemService(DisplayManager.class)
+                    .registerDisplayListener(this, mWorker.getThreadHandler());
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) { }
+
+        @Override
+        public void onDisplayRemoved(int displayId) { }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId == getDisplayContext().getDisplayId()) {
+                mDisplaySizeValid = false;
+            }
         }
 
         EglHelper getEglHelperInstance() {
@@ -154,26 +165,10 @@
             if (pages == mPages) return;
             mPages = pages;
             if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
-            updateShift();
             mWorker.getThreadHandler().post(() ->
                     computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
         }
 
-        private void updateShift() {
-            if (mImgHeight == 0) {
-                mPageOffset = 0;
-                mPageWidth = 1;
-                return;
-            }
-            // calculate shift
-            DisplayInfo displayInfo = new DisplayInfo();
-            getDisplayContext().getDisplay().getDisplayInfo(displayInfo);
-            int screenWidth = displayInfo.getNaturalWidth();
-            float imgWidth = Math.min(mImgWidth > 0 ? screenWidth / (float) mImgWidth : 1.f, 1.f);
-            mPageWidth = imgWidth;
-            mPageOffset = (1 - imgWidth) / (float) (mPages - 1);
-        }
-
         private void updateMiniBitmap(Bitmap b) {
             if (b == null) return;
             int size = Math.min(b.getWidth(), b.getHeight());
@@ -204,6 +199,8 @@
 
         @Override
         public void onDestroy() {
+            getDisplayContext().getSystemService(DisplayManager.class)
+                    .unregisterDisplayListener(this);
             mMiniBitmap = null;
             mWorker.getThreadHandler().post(() -> {
                 mRenderer.finish();
@@ -268,6 +265,16 @@
          * (1-Wr)].
          */
         private RectF pageToImgRect(RectF area) {
+            if (!mDisplaySizeValid) {
+                Rect window = getDisplayContext()
+                        .getSystemService(WindowManager.class)
+                        .getCurrentWindowMetrics()
+                        .getBounds();
+                mDisplayWidth = window.width();
+                mDisplayHeight = window.height();
+                mDisplaySizeValid = true;
+            }
+
             // Width of a page for the caller of this API.
             float virtualPageWidth = 1f / (float) mPages;
             float leftPosOnPage = (area.left % virtualPageWidth) / virtualPageWidth;
@@ -275,12 +282,24 @@
             int currentPage = (int) Math.floor(area.centerX() / virtualPageWidth);
 
             RectF imgArea = new RectF();
+
+            if (mImgWidth == 0 || mImgHeight == 0 || mDisplayWidth <= 0 || mDisplayHeight <= 0) {
+                return imgArea;
+            }
+
             imgArea.bottom = area.bottom;
             imgArea.top = area.top;
+
+            float imageScale = Math.min(((float) mImgHeight) / mDisplayHeight, 1);
+            float mappedScreenWidth = mDisplayWidth * imageScale;
+            float pageWidth = Math.min(1.0f,
+                    mImgWidth > 0 ? mappedScreenWidth / (float) mImgWidth : 1.f);
+            float pageOffset = (1 - pageWidth) / (float) (mPages - 1);
+
             imgArea.left = MathUtils.constrain(
-                    leftPosOnPage * mPageWidth + currentPage * mPageOffset, 0, 1);
+                    leftPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
             imgArea.right = MathUtils.constrain(
-                    rightPosOnPage * mPageWidth + currentPage * mPageOffset, 0, 1);
+                    rightPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
             if (imgArea.left > imgArea.right) {
                 // take full page
                 imgArea.left = 0;
@@ -293,7 +312,6 @@
         private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
                 Bitmap b) {
             List<WallpaperColors> colors = new ArrayList<>(areas.size());
-            updateShift();
             for (int i = 0; i < areas.size(); i++) {
                 RectF area = pageToImgRect(areas.get(i));
                 if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 17178fa..e521c90 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -310,7 +310,8 @@
     }
 
     void onConfigurationChanged(int configDiff) {
-        if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+        if ((configDiff & (ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE))
+                != 0) {
             final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
             mDraggableWindowBounds.set(getDraggableWindowBounds());
             // Keep the Y position with the same height ratio before the window bounds and
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index ca2c034..2f88291 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -59,6 +59,7 @@
 import com.android.systemui.util.Assert;
 
 import java.util.Locale;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -139,10 +140,10 @@
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     private final SystemActionsBroadcastReceiver mReceiver;
-    private final Recents mRecents;
+    private final Optional<Recents> mRecentsOptional;
     private Locale mLocale;
     private final AccessibilityManager mA11yManager;
-    private final Lazy<StatusBar> mStatusBar;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final NotificationShadeWindowController mNotificationShadeController;
     private final StatusBarWindowCallback mNotificationShadeCallback;
     private boolean mDismissNotificationShadeActionRegistered;
@@ -150,10 +151,10 @@
     @Inject
     public SystemActions(Context context,
             NotificationShadeWindowController notificationShadeController,
-            Lazy<StatusBar> statusBar,
-            Recents recents) {
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+            Optional<Recents> recentsOptional) {
         super(context);
-        mRecents = recents;
+        mRecentsOptional = recentsOptional;
         mReceiver = new SystemActionsBroadcastReceiver();
         mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
         mA11yManager = (AccessibilityManager) mContext.getSystemService(
@@ -161,9 +162,9 @@
         mNotificationShadeController = notificationShadeController;
         // Saving in instance variable since to prevent GC since
         // NotificationShadeWindowController.registerCallback() only keeps weak references.
-        mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+        mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
                 registerOrUnregisterDismissNotificationShadeAction();
-        mStatusBar = statusBar;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
     }
 
     @Override
@@ -242,8 +243,9 @@
 
         // Saving state in instance variable since this callback is called quite often to avoid
         // binder calls
-        StatusBar statusBar = mStatusBar.get();
-        if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+        final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+        if (statusBarOptional.map(StatusBar::isPanelExpanded).orElse(false)
+                && !statusBarOptional.get().isKeyguardShowing()) {
             if (!mDismissNotificationShadeActionRegistered) {
                 mA11yManager.registerSystemAction(
                         createRemoteAction(
@@ -368,15 +370,16 @@
     }
 
     private void handleRecents() {
-        mRecents.toggleRecentApps();
+        mRecentsOptional.ifPresent(Recents::toggleRecentApps);
     }
 
     private void handleNotifications() {
-        mStatusBar.get().animateExpandNotificationsPanel();
+        mStatusBarOptionalLazy.get().ifPresent(StatusBar::animateExpandNotificationsPanel);
     }
 
     private void handleQuickSettings() {
-        mStatusBar.get().animateExpandSettingsPanel(null);
+        mStatusBarOptionalLazy.get().ifPresent(
+                statusBar -> statusBar.animateExpandSettingsPanel(null));
     }
 
     private void handlePowerDialog() {
@@ -425,7 +428,9 @@
     }
 
     private void handleAccessibilityDismissNotificationShade() {
-        mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+        mStatusBarOptionalLazy.get().ifPresent(
+                statusBar -> statusBar.animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
     }
 
     private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index cee395b..3281347 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -91,7 +91,7 @@
             final Context windowContext = mContext.createWindowContext(display,
                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
             final WindowMagnificationController controller = new WindowMagnificationController(
-                    mContext,
+                    windowContext,
                     mHandler, new SfVsyncFrameCallbackProvider(), null,
                     new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
             return new WindowMagnificationAnimationController(windowContext, controller);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 717c715..a51e3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -28,6 +28,7 @@
 import android.annotation.UiContext;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -35,6 +36,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -76,6 +78,8 @@
         MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
 
     private static final String TAG = "WindowMagnificationController";
+    @SuppressWarnings("isloggabletaglength")
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE;
     // Delay to avoid updating state description too frequently.
     private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100;
     // It should be consistent with the value defined in WindowMagnificationGestureHandler.
@@ -158,14 +162,15 @@
         mRotation = display.getRotation();
 
         mWm = context.getSystemService(WindowManager.class);
-        mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+        mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
 
         mResources = mContext.getResources();
         mScale = mResources.getInteger(R.integer.magnification_default_scale);
         mBounceEffectDuration = mResources.getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         updateDimensions();
-        setInitialStartBounds();
+        setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
+                mWindowBounds.height() / 2);
         computeBounceAnimationScale();
 
         mMirrorWindowControl = mirrorWindowControl;
@@ -286,18 +291,59 @@
      * @param configDiff a bit mask of the differences between the configurations
      */
     void onConfigurationChanged(int configDiff) {
+        if (DEBUG) {
+            Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString(
+                    configDiff));
+        }
+        if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+            onRotate();
+        }
+
+        if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            updateAccessibilityWindowTitleIfNeeded();
+        }
+
+        boolean reCreateWindow = false;
         if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
             updateDimensions();
             computeBounceAnimationScale();
-            if (isWindowVisible()) {
-                deleteWindowMagnification();
-                enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
-            }
-        } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
-            onRotate();
-        } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
-            updateAccessibilityWindowTitleIfNeeded();
+            reCreateWindow = true;
         }
+
+        if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            reCreateWindow |= handleScreenSizeChanged();
+        }
+
+        // Recreate the window again to correct the window appearance due to density or
+        // window size changed not caused by rotation.
+        if (isWindowVisible() && reCreateWindow) {
+            deleteWindowMagnification();
+            enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+        }
+    }
+
+    /**
+     * Calculates the magnification frame if the window bounds is changed.
+     * Note that the orientation also changes the wind bounds, so it should be handled first.
+     *
+     * @return {@code true} if the magnification frame is changed with the new window bounds.
+     */
+    private boolean handleScreenSizeChanged() {
+        final Rect oldWindowBounds = new Rect(mWindowBounds);
+        final Rect currentWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+
+        if (currentWindowBounds.equals(oldWindowBounds)) {
+            if (DEBUG) {
+                Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed");
+            }
+            return false;
+        }
+        mWindowBounds.set(currentWindowBounds);
+        final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
+        final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
+        setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+        calculateMagnificationFrameBoundary();
+        return true;
     }
 
     private void updateSystemUIStateIfNeeded() {
@@ -311,30 +357,42 @@
         mWm.updateViewLayout(mMirrorView, params);
     }
 
-    /** Handles MirrorWindow position when the device rotation changed. */
+    /**
+     * Keep MirrorWindow position on the screen unchanged when device rotates 90° clockwise or
+     * anti-clockwise.
+     */
     private void onRotate() {
         final Display display = mContext.getDisplay();
         final int oldRotation = mRotation;
-        mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
-
-        setMagnificationFrameBoundary();
         mRotation = display.getRotation();
+        final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+        if (rotationDegree == 0 || rotationDegree == 180) {
+            Log.w(TAG, "onRotate -- rotate with the device. skip it");
+            return;
+        }
+        final Rect currentWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
+        if (currentWindowBounds.width() != mWindowBounds.height()
+                || currentWindowBounds.height() != mWindowBounds.width()) {
+            Log.w(TAG, "onRotate -- unexpected window height/width");
+            return;
+        }
+
+        mWindowBounds.set(currentWindowBounds);
+
+        calculateMagnificationFrameBoundary();
 
         if (!isWindowVisible()) {
             return;
         }
         // Keep MirrorWindow position on the screen unchanged when device rotates 90°
         // clockwise or anti-clockwise.
-        final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+
         final Matrix matrix = new Matrix();
         matrix.setRotate(rotationDegree);
         if (rotationDegree == 90) {
             matrix.postTranslate(mWindowBounds.width(), 0);
         } else if (rotationDegree == 270) {
             matrix.postTranslate(0, mWindowBounds.height());
-        } else {
-            Log.w(TAG, "Invalid rotation change. " + rotationDegree);
-            return;
         }
         // The rect of MirrorView is going to be transformed.
         LayoutParams params =
@@ -440,12 +498,12 @@
         }
     }
 
-    private void setInitialStartBounds() {
+    private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
         // Sets the initial frame area for the mirror and places it in the center of the display.
-        final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 2
+        final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2
                 + 2 * mMirrorSurfaceMargin;
-        final int initX = mWindowBounds.width() / 2 - initSize / 2;
-        final int initY = mWindowBounds.height() / 2 - initSize / 2;
+        final int initX = centerX - initSize / 2;
+        final int initY = centerY - initSize / 2;
         mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
     }
 
@@ -553,7 +611,7 @@
         mSourceBounds.set(left, top, right, bottom);
     }
 
-    private void setMagnificationFrameBoundary() {
+    private void calculateMagnificationFrameBoundary() {
         // Calculates width and height for magnification frame could exceed out the screen.
         // TODO : re-calculating again when scale is changed.
         // The half width of magnification frame.
@@ -644,7 +702,7 @@
                 : centerY - mMagnificationFrame.exactCenterY();
         mScale = Float.isNaN(scale) ? mScale : scale;
 
-        setMagnificationFrameBoundary();
+        calculateMagnificationFrameBoundary();
         updateMagnificationFramePosition((int) offsetX, (int) offsetY);
         if (!isWindowVisible()) {
             createMirrorWindow();
@@ -764,6 +822,8 @@
         pw.println("      mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
         pw.println("      mScale:" + mScale);
         pw.println("      mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+        pw.println("      mSourceBounds:"
+                 + (isWindowVisible() ? mSourceBounds : "empty"));
         pw.println("      mSystemGestureTop:" + mSystemGestureTop);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 169a9c0..f13730e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -70,7 +70,7 @@
     };
 
     private final Context mContext;
-    private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final StatusBarStateController mStatusBarStateController;
 
     private boolean mLauncherShowing;
@@ -78,7 +78,7 @@
 
     @Inject
     PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
-            Optional<Lazy<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
         mContext = context;
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
@@ -180,8 +180,7 @@
     }
 
     private boolean isBouncerShowing() {
-        return mStatusBarOptionalLazy.map(
-                statusBarLazy -> statusBarLazy.get().isBouncerShowing()).orElse(false);
+        return mStatusBarOptionalLazy.get().map(StatusBar::isBouncerShowing).orElse(false);
     }
 
     private boolean isKeyguardLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index b7344fb..a2e55c0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -28,6 +28,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Optional;
 
 /**
  * Handles:
@@ -42,7 +43,7 @@
 abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
         extends ViewController<T> implements Dumpable {
     @NonNull final StatusBarStateController mStatusBarStateController;
-    @NonNull final StatusBar mStatusBar;
+    @NonNull final Optional<StatusBar> mStatusBarOptional;
     @NonNull final DumpManager mDumpManger;
 
     boolean mNotificationShadeExpanded;
@@ -50,11 +51,11 @@
     protected UdfpsAnimationViewController(
             T view,
             @NonNull StatusBarStateController statusBarStateController,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull DumpManager dumpManager) {
         super(view);
         mStatusBarStateController = statusBarStateController;
-        mStatusBar = statusBar;
+        mStatusBarOptional = statusBarOptional;
         mDumpManger = dumpManager;
     }
 
@@ -62,13 +63,17 @@
 
     @Override
     protected void onViewAttached() {
-        mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.addExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         mDumpManger.registerDumpable(getDumpTag(), this);
     }
 
     @Override
     protected void onViewDetached() {
-        mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.removeExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         mDumpManger.unregisterDumpable(getDumpTag());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 93d80e2..85955e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -22,6 +22,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import java.util.Optional;
+
 /**
  * Class that coordinates non-HBM animations for biometric prompt.
  */
@@ -29,9 +31,9 @@
     protected UdfpsBpViewController(
             @NonNull UdfpsBpView view,
             @NonNull StatusBarStateController statusBarStateController,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, statusBar, dumpManager);
+        super(view, statusBarStateController, statusBarOptional, dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 950bd83..2309d5f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -108,7 +108,7 @@
     @NonNull private final LayoutInflater mInflater;
     private final WindowManager mWindowManager;
     private final DelayableExecutor mFgExecutor;
-    @NonNull private final StatusBar mStatusBar;
+    @NonNull private final Optional<StatusBar> mStatusBarOptional;
     @NonNull private final StatusBarStateController mStatusBarStateController;
     @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@@ -506,7 +506,7 @@
             @NonNull WindowManager windowManager,
             @NonNull StatusBarStateController statusBarStateController,
             @Main DelayableExecutor fgExecutor,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -534,7 +534,7 @@
         mFingerprintManager = checkNotNull(fingerprintManager);
         mWindowManager = windowManager;
         mFgExecutor = fgExecutor;
-        mStatusBar = statusBar;
+        mStatusBarOptional = statusBarOptional;
         mStatusBarStateController = statusBarStateController;
         mKeyguardStateController = keyguardStateController;
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -759,7 +759,7 @@
                         enrollView,
                         mServerRequest.mEnrollHelper,
                         mStatusBarStateController,
-                        mStatusBar,
+                        mStatusBarOptional,
                         mDumpManager
                 );
             case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
@@ -769,7 +769,7 @@
                 return new UdfpsKeyguardViewController(
                         keyguardView,
                         mStatusBarStateController,
-                        mStatusBar,
+                        mStatusBarOptional,
                         mKeyguardViewManager,
                         mKeyguardUpdateMonitor,
                         mFgExecutor,
@@ -785,7 +785,7 @@
                 return new UdfpsBpViewController(
                         bpView,
                         mStatusBarStateController,
-                        mStatusBar,
+                        mStatusBarOptional,
                         mDumpManager
                 );
             case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
@@ -795,7 +795,7 @@
                 return new UdfpsFpmOtherViewController(
                         authOtherView,
                         mStatusBarStateController,
-                        mStatusBar,
+                        mStatusBarOptional,
                         mDumpManager
                 );
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 3dab010..54244a1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -24,6 +24,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import java.util.Optional;
+
 /**
  * Class that coordinates non-HBM animations during enrollment.
  */
@@ -48,9 +50,9 @@
             @NonNull UdfpsEnrollView view,
             @NonNull UdfpsEnrollHelper enrollHelper,
             @NonNull StatusBarStateController statusBarStateController,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, statusBar, dumpManager);
+        super(view, statusBarStateController, statusBarOptional, dumpManager);
         mEnrollProgressBarRadius = getContext().getResources()
                 .getInteger(R.integer.config_udfpsEnrollProgressBar);
         mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6e2e4ba..dcb5aef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -22,6 +22,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import java.util.Optional;
+
 /**
  * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
  * states.
@@ -32,9 +34,9 @@
     protected UdfpsFpmOtherViewController(
             @NonNull UdfpsFpmOtherView view,
             @NonNull StatusBarStateController statusBarStateController,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, statusBar, dumpManager);
+        super(view, statusBarStateController, statusBarOptional, dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 6435bdd..9c95720 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -40,6 +40,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Optional;
 
 
 /**
@@ -80,7 +81,7 @@
     protected UdfpsKeyguardViewController(
             @NonNull UdfpsKeyguardView view,
             @NonNull StatusBarStateController statusBarStateController,
-            @NonNull StatusBar statusBar,
+            @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
             @NonNull DelayableExecutor mainDelayableExecutor,
@@ -88,7 +89,7 @@
             @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull LockscreenShadeTransitionController transitionController,
             @NonNull UdfpsController udfpsController) {
-        super(view, statusBarStateController, statusBar, dumpManager);
+        super(view, statusBarStateController, statusBarOptional, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mExecutor = mainDelayableExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
new file mode 100644
index 0000000..6e2fcd1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Container for communal presentation. Containing communal-related view to this parent view allows
+ * for aggregate measurement/layout adjustments and capturing said values before the communal views
+ * might be available.
+ */
+public class CommunalHostView extends FrameLayout {
+    public CommunalHostView(@NonNull Context context) {
+        this(context, null, 0);
+    }
+
+    public CommunalHostView(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CommunalHostView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
new file mode 100644
index 0000000..6592015
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+import android.view.View;
+
+import com.android.systemui.util.ViewController;
+
+/**
+ * Injectable controller for {@link CommunalHostView}.
+ */
+public class CommunalHostViewController extends ViewController<CommunalHostView> {
+    protected CommunalHostViewController(CommunalHostView view) {
+        super(view);
+    }
+
+    @Override
+    protected void onViewAttached() {
+    }
+
+    @Override
+    protected void onViewDetached() {
+    }
+
+    /**
+     * Sets whether the {@link CommunalHostView} is visible.
+     *
+     * @param visible {@code true} if the view should be shown, {@code false} otherwise.
+     */
+    public void show(boolean visible) {
+        mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
new file mode 100644
index 0000000..bcc3684
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal.dagger;
+
+import dagger.Module;
+
+/**
+ * Dagger Module providing Communal-related functionality.
+ */
+@Module(subcomponents = {
+        CommunalViewComponent.class,
+})
+public class CommunalModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java
new file mode 100644
index 0000000..3a80a03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal.dagger;
+
+import com.android.systemui.communal.CommunalHostView;
+import com.android.systemui.communal.CommunalHostViewController;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for working with {@link CommunalHostView}.
+ */
+@Subcomponent
+public interface CommunalViewComponent {
+    /** Simple factory for {@link CommunalViewComponent}. */
+    @Subcomponent.Factory
+    interface Factory {
+        CommunalViewComponent build(@BindsInstance CommunalHostView view);
+    }
+
+    /** Builds a {@link CommunalHostViewController}. */
+    CommunalHostViewController getCommunalHostViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 567d0cb..557bca8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@
     private val onSeedingComplete = Consumer<Boolean> {
         accepted ->
             if (accepted) {
-                selectedStructure = controlsController.get().getFavorites().maxBy {
+                selectedStructure = controlsController.get().getFavorites().maxByOrNull {
                     it.controls.size
                 } ?: EMPTY_STRUCTURE
                 updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c97a30e..2d200e3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.om.OverlayManager;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.ColorDisplayManager;
 import android.os.Handler;
@@ -59,10 +61,13 @@
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.LifecycleScreenStatusProvider;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarA11yHelper;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarOverlayController;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.TaskbarDelegate;
 import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.ReduceBrightColorsController;
@@ -75,6 +80,9 @@
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.unfold.UnfoldTransitionFactory;
+import com.android.unfold.UnfoldTransitionProgressProvider;
+import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -225,7 +233,7 @@
             Optional<Pip> pipOptional,
             Optional<LegacySplitScreen> splitScreenOptional,
             Optional<Recents> recentsOptional,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             ShadeController shadeController,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationShadeDepthController notificationShadeDepthController,
@@ -234,6 +242,8 @@
             UiEventLogger uiEventLogger,
             NavigationBarOverlayController navBarOverlayController,
             ConfigurationController configurationController,
+            NavigationBarA11yHelper navigationBarA11yHelper,
+            TaskbarDelegate taskbarDelegate,
             UserTracker userTracker) {
         return new NavigationBarController(context,
                 windowManager,
@@ -252,7 +262,7 @@
                 pipOptional,
                 splitScreenOptional,
                 recentsOptional,
-                statusBarLazy,
+                statusBarOptionalLazy,
                 shadeController,
                 notificationRemoteInputManager,
                 notificationShadeDepthController,
@@ -261,6 +271,8 @@
                 uiEventLogger,
                 navBarOverlayController,
                 configurationController,
+                navigationBarA11yHelper,
+                taskbarDelegate,
                 userTracker);
     }
 
@@ -372,6 +384,37 @@
     /** */
     @Provides
     @SysUISingleton
+    public UnfoldTransitionProgressProvider provideUnfoldTransitionProgressProvider(
+            Context context,
+            UnfoldTransitionConfig config,
+            LifecycleScreenStatusProvider screenStatusProvider,
+            DeviceStateManager deviceStateManager,
+            SensorManager sensorManager,
+            @Main Executor executor,
+            @Main Handler handler
+    ) {
+        return UnfoldTransitionFactory
+                .createUnfoldTransitionProgressProvider(
+                        context,
+                        config,
+                        screenStatusProvider,
+                        deviceStateManager,
+                        sensorManager,
+                        handler,
+                        executor
+                );
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    public UnfoldTransitionConfig provideUnfoldTransitionConfig(Context context) {
+        return UnfoldTransitionFactory.createConfig(context);
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
     public Choreographer providesChoreographer() {
         return Choreographer.getInstance();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 954ba79..4d1608f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources;
 import android.hardware.SensorManager;
 import android.hardware.SensorPrivacyManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.face.FaceManager;
@@ -159,6 +160,12 @@
 
     @Provides
     @Singleton
+    static DeviceStateManager provideDeviceStateManager(Context context) {
+        return context.getSystemService(DeviceStateManager.class);
+    }
+
+    @Provides
+    @Singleton
     static IActivityManager provideIActivityManager() {
         return ActivityManager.getService();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 1ed8819..40768ec 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -23,6 +23,7 @@
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.accessibility.WindowMagnification;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.communal.dagger.CommunalModule;
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -51,7 +52,12 @@
 /**
  * SystemUI objects that are injectable should go here.
  */
-@Module(includes = {RecentsModule.class, StatusBarModule.class, KeyguardModule.class})
+@Module(includes = {
+        RecentsModule.class,
+        StatusBarModule.class,
+        KeyguardModule.class,
+        CommunalModule.class,
+})
 public abstract class SystemUIBinder {
     /** Inject into AuthController. */
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index bc4ced4..04a0226 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -81,6 +81,7 @@
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -172,7 +173,7 @@
             SysUiState sysUiState,
             @Main Handler handler,
             PackageManager packageManager,
-            StatusBar statusBar) {
+            Optional<StatusBar> statusBarOptional) {
 
         super(context,
                 windowManagerFuncs,
@@ -199,12 +200,11 @@
                 iWindowManager,
                 backgroundExecutor,
                 uiEventLogger,
-                null,
                 ringerModeTracker,
                 sysUiState,
                 handler,
                 packageManager,
-                statusBar);
+                statusBarOptional);
 
         mLockPatternUtils = lockPatternUtils;
         mKeyguardStateController = keyguardStateController;
@@ -334,12 +334,11 @@
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
-                StatusBar statusBar) {
+                Optional<StatusBar> statusBarOptional) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
                     adapter, overflowAdapter, sysuiColorExtractor, statusBarService,
                     notificationShadeWindowController, sysuiState, onRotateCallback,
-                    keyguardShowing, powerAdapter, uiEventLogger, null,
-                    statusBar);
+                    keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional);
             mWalletFactory = walletFactory;
 
             // Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 06e7482..b4e0e2f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -131,6 +131,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -190,7 +191,6 @@
     private final MetricsLogger mMetricsLogger;
     private final UiEventLogger mUiEventLogger;
     private final SysUiState mSysUiState;
-    private final GlobalActionsInfoProvider mInfoProvider;
 
     // Used for RingerModeTracker
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -230,7 +230,7 @@
     private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
     protected Handler mMainHandler;
     private int mSmallestScreenWidthDp;
-    private final StatusBar mStatusBar;
+    private final Optional<StatusBar> mStatusBarOptional;
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -333,12 +333,11 @@
             IWindowManager iWindowManager,
             @Background Executor backgroundExecutor,
             UiEventLogger uiEventLogger,
-            GlobalActionsInfoProvider infoProvider,
             RingerModeTracker ringerModeTracker,
             SysUiState sysUiState,
             @Main Handler handler,
             PackageManager packageManager,
-            StatusBar statusBar) {
+            Optional<StatusBar> statusBarOptional) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -358,7 +357,6 @@
         mTelecomManager = telecomManager;
         mMetricsLogger = metricsLogger;
         mUiEventLogger = uiEventLogger;
-        mInfoProvider = infoProvider;
         mSysuiColorExtractor = colorExtractor;
         mStatusBarService = statusBarService;
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -368,7 +366,7 @@
         mSysUiState = sysUiState;
         mMainHandler = handler;
         mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
-        mStatusBar = statusBar;
+        mStatusBarOptional = statusBarOptional;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -418,8 +416,8 @@
         return mUiEventLogger;
     }
 
-    protected StatusBar getStatusBar() {
-        return mStatusBar;
+    protected Optional<StatusBar> getStatusBar() {
+        return mStatusBarOptional;
     }
 
     /**
@@ -653,7 +651,7 @@
                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
                 mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
-                mInfoProvider, mStatusBar);
+                mStatusBarOptional);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -850,7 +848,7 @@
             mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
             if (mTelecomManager != null) {
                 // Close shade so user sees the activity
-                mStatusBar.collapseShade();
+                mStatusBarOptional.ifPresent(StatusBar::collapseShade);
                 Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
                         null /* number */);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -982,7 +980,7 @@
                             mIActivityManager.requestInteractiveBugReport();
                         }
                         // Close shade so user sees the activity
-                        mStatusBar.collapseShade();
+                        mStatusBarOptional.ifPresent(StatusBar::collapseShade);
                     } catch (RemoteException e) {
                     }
                 }
@@ -1002,7 +1000,7 @@
                 mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
                 mIActivityManager.requestFullBugReport();
                 // Close shade so user sees the activity
-                mStatusBar.collapseShade();
+                mStatusBarOptional.ifPresent(StatusBar::collapseShade);
             } catch (RemoteException e) {
             }
             return false;
@@ -2119,9 +2117,8 @@
         private Dialog mPowerOptionsDialog;
         protected final Runnable mOnRotateCallback;
         private UiEventLogger mUiEventLogger;
-        private GlobalActionsInfoProvider mInfoProvider;
         private GestureDetector mGestureDetector;
-        private StatusBar mStatusBar;
+        private Optional<StatusBar> mStatusBarOptional;
 
         protected ViewGroup mContainer;
 
@@ -2146,7 +2143,8 @@
                     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                             float distanceY) {
                         if (distanceY < 0 && distanceY > distanceX
-                                && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+                                && e1.getY() <= mStatusBarOptional.map(
+                                        StatusBar::getStatusBarHeight).orElse(0)) {
                             // Downwards scroll from top
                             openShadeAndDismiss();
                             return true;
@@ -2158,7 +2156,8 @@
                     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                             float velocityY) {
                         if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
-                                && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+                                && e1.getY() <= mStatusBarOptional.map(
+                                        StatusBar::getStatusBarHeight).orElse(0)) {
                             // Downwards fling from top
                             openShadeAndDismiss();
                             return true;
@@ -2173,7 +2172,7 @@
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
-                @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
+                Optional<StatusBar> statusBarOptional) {
             super(context, themeRes);
             mContext = context;
             mAdapter = adapter;
@@ -2186,8 +2185,7 @@
             mOnRotateCallback = onRotateCallback;
             mKeyguardShowing = keyguardShowing;
             mUiEventLogger = uiEventLogger;
-            mInfoProvider = infoProvider;
-            mStatusBar = statusBar;
+            mStatusBarOptional = statusBarOptional;
 
             mGestureDetector = new GestureDetector(mContext, mGestureListener);
 
@@ -2218,12 +2216,14 @@
 
         private void openShadeAndDismiss() {
             mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
-            if (mStatusBar.isKeyguardShowing()) {
+            if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
                 // match existing lockscreen behavior to open QS when swiping from status bar
-                mStatusBar.animateExpandSettingsPanel(null);
+                mStatusBarOptional.ifPresent(
+                        statusBar -> statusBar.animateExpandSettingsPanel(null));
             } else {
                 // otherwise, swiping down should expand notification shade
-                mStatusBar.animateExpandNotificationsPanel();
+                mStatusBarOptional.ifPresent(
+                        statusBar -> statusBar.animateExpandNotificationsPanel());
             }
             dismiss();
         }
@@ -2304,10 +2304,6 @@
                 mBackgroundDrawable = new ScrimDrawable();
                 mScrimAlpha = 1.0f;
             }
-
-            if (mInfoProvider != null && mInfoProvider.shouldShowMessage()) {
-                mInfoProvider.addPanel(mContext, mContainer, mAdapter.getCount(), () -> dismiss());
-            }
         }
 
         protected void fixNavBarClipping() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
deleted file mode 100644
index 25837e3..0000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.globalactions
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.content.res.Configuration
-import android.net.Uri
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-private const val TAG = "GlobalActionsInfo"
-
-/** Maximum number of times to show change info message  */
-private const val MAX_VIEW_COUNT = 3
-
-/** Maximum number of buttons allowed in landscape before this panel does not fit */
-private const val MAX_BUTTONS_LANDSCAPE = 4
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-class GlobalActionsInfoProvider @Inject constructor(
-    private val context: Context,
-    private val walletClient: QuickAccessWalletClient,
-    private val controlsController: ControlsController,
-    private val activityStarter: ActivityStarter
-) {
-
-    private var pendingIntent: PendingIntent
-
-    init {
-        val url = context.resources.getString(R.string.global_actions_change_url)
-        val intent = Intent(Intent.ACTION_VIEW).apply {
-            setData(Uri.parse(url))
-            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        }
-        pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
-    }
-
-    fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) {
-        // This panel does not fit on landscape with two rows of buttons showing,
-        // so skip adding the panel (and incrementing view counT) in that case
-        val isLandscape =
-                context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
-        if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) {
-            return
-        }
-
-        val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
-                parent, false)
-
-        val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
-        val message = view.findViewById<TextView>(R.id.global_actions_change_message)
-        message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
-
-        view.setOnClickListener { _ ->
-            dismissParent.run()
-            activityStarter.postStartActivityDismissingKeyguard(pendingIntent)
-        }
-        parent.addView(view, 0) // Add to top
-        incrementViewCount()
-    }
-
-    fun shouldShowMessage(): Boolean {
-        // This is only relevant for some devices
-        val isEligible = context.resources.getBoolean(
-                R.bool.global_actions_show_change_info)
-        if (!isEligible) {
-            return false
-        }
-
-        val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
-
-        // Only show to users who previously had these items set up
-        val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) {
-            sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
-        } else {
-            -1
-        }
-
-        // Limit number of times this is displayed
-        return viewCount > -1 && viewCount < MAX_VIEW_COUNT
-    }
-
-    private fun hadContent(): Boolean {
-        // Check whether user would have seen content in the power menu that has now moved
-        val hadControls = controlsController.getFavorites().size > 0
-        val hadCards = walletClient.isWalletFeatureAvailable
-        Log.d(TAG, "Previously had controls $hadControls, cards $hadCards")
-        return hadControls || hadCards
-    }
-
-    private fun incrementViewCount() {
-        val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
-        val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
-        sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
new file mode 100644
index 0000000..d8905a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.keyguard
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.unfold.updates.screen.ScreenStatusProvider
+import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import javax.inject.Inject
+
+@SysUISingleton
+class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenLifecycle) :
+    ScreenStatusProvider, ScreenLifecycle.Observer {
+
+    init {
+        screenLifecycle.addObserver(this)
+    }
+
+    private val listeners: MutableList<ScreenListener> = mutableListOf()
+
+    override fun removeCallback(listener: ScreenListener) {
+        listeners.remove(listener)
+    }
+
+    override fun addCallback(listener: ScreenListener) {
+        listeners.add(listener)
+    }
+
+    override fun onScreenTurnedOn() {
+        listeners.forEach(ScreenListener::onScreenTurnedOn)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 2bf102f7..5ff624d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -24,7 +24,6 @@
 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.FeatureFlags
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -45,7 +44,6 @@
     private val bypassController: KeyguardBypassController,
     private val statusBarStateController: SysuiStatusBarStateController,
     private val notifLockscreenUserManager: NotificationLockscreenUserManager,
-    private val featureFlags: FeatureFlags,
     private val context: Context,
     configurationController: ConfigurationController
 ) {
@@ -73,7 +71,7 @@
     }
 
     private fun updateResources() {
-        useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+        useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index cb11454..683b5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -23,10 +23,12 @@
 import static android.app.StatusBarManager.WindowType;
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.containsType;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
@@ -40,10 +42,12 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -54,9 +58,7 @@
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IdRes;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
@@ -68,6 +70,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -89,6 +92,7 @@
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -101,7 +105,6 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.VisibleForTesting;
@@ -130,6 +133,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.AutoHideUiElement;
@@ -151,7 +155,6 @@
 import com.android.wm.shell.pip.Pip;
 
 import java.io.PrintWriter;
-import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.function.Consumer;
@@ -162,8 +165,7 @@
  * Contains logic for a navigation bar view.
  */
 public class NavigationBar implements View.OnAttachStateChangeListener,
-        Callbacks, NavigationModeController.ModeChangedListener,
-        AccessibilityButtonModeObserver.ModeChangedListener {
+        Callbacks, NavigationModeController.ModeChangedListener {
 
     public static final String TAG = "NavigationBar";
     private static final boolean DEBUG = false;
@@ -180,13 +182,12 @@
     private final Context mContext;
     private final WindowManager mWindowManager;
     private final AccessibilityManager mAccessibilityManager;
-    private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final StatusBarStateController mStatusBarStateController;
     private final MetricsLogger mMetricsLogger;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final SysUiState mSysUiFlagsContainer;
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final ShadeController mShadeController;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final OverviewProxyService mOverviewProxyService;
@@ -201,11 +202,13 @@
     private final Handler mHandler;
     private final NavigationBarOverlayController mNavbarOverlayController;
     private final UiEventLogger mUiEventLogger;
+    private final NavigationBarA11yHelper mNavigationBarA11yHelper;
     private final UserTracker mUserTracker;
     private final NotificationShadeDepthController mNotificationShadeDepthController;
 
     private Bundle mSavedState;
     private NavigationBarView mNavigationBarView;
+    private NavigationBarFrame mFrame;
 
     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
 
@@ -381,6 +384,13 @@
         }
 
         @Override
+        public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+            mNavigationBarView
+                    .getFloatingRotationButton()
+                    .onTaskbarStateChanged(visible, stashed);
+        }
+
+        @Override
         public void onToggleRecentApps() {
             // The same case as onOverviewShown but only for 3-button navigation.
             mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
@@ -475,7 +485,8 @@
             CommandQueue commandQueue,
             Optional<Pip> pipOptional,
             Optional<LegacySplitScreen> splitScreenOptional,
-            Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
+            Optional<Recents> recentsOptional,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             ShadeController shadeController,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationShadeDepthController notificationShadeDepthController,
@@ -483,17 +494,17 @@
             @Main Handler mainHandler,
             NavigationBarOverlayController navbarOverlayController,
             UiEventLogger uiEventLogger,
+            NavigationBarA11yHelper navigationBarA11yHelper,
             UserTracker userTracker) {
         mContext = context;
         mWindowManager = windowManager;
         mAccessibilityManager = accessibilityManager;
-        mAccessibilityManagerWrapper = accessibilityManagerWrapper;
         mDeviceProvisionedController = deviceProvisionedController;
         mStatusBarStateController = statusBarStateController;
         mMetricsLogger = metricsLogger;
         mAssistManagerLazy = assistManagerLazy;
         mSysUiFlagsContainer = sysUiFlagsContainer;
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mShadeController = shadeController;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mOverviewProxyService = overviewProxyService;
@@ -508,11 +519,11 @@
         mHandler = mainHandler;
         mNavbarOverlayController = navbarOverlayController;
         mUiEventLogger = uiEventLogger;
+        mNavigationBarA11yHelper = navigationBarA11yHelper;
         mUserTracker = userTracker;
         mNotificationShadeDepthController = notificationShadeDepthController;
 
         mNavBarMode = mNavigationModeController.addListener(this);
-        mAccessibilityButtonModeObserver.addListener(this);
     }
 
     public NavigationBarView getView() {
@@ -520,34 +531,17 @@
     }
 
     public View createView(Bundle savedState) {
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
-                PixelFormat.TRANSLUCENT);
-        lp.token = new Binder();
-        lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
-        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        lp.windowAnimations = 0;
-        lp.setTitle("NavigationBar" + mContext.getDisplayId());
-        lp.setFitInsetsTypes(0 /* types */);
-        lp.setTrustedOverlay();
-
-        NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
+        mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
                 R.layout.navigation_bar_window, null);
-        View barView = LayoutInflater.from(frame.getContext()).inflate(
-                R.layout.navigation_bar, frame);
+        View barView = LayoutInflater.from(mFrame.getContext()).inflate(
+                R.layout.navigation_bar, mFrame);
         barView.addOnAttachStateChangeListener(this);
         mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
 
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
-        mContext.getSystemService(WindowManager.class).addView(frame, lp);
+        mContext.getSystemService(WindowManager.class).addView(mFrame,
+                getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
+                        .getRotation()));
         mDisplayId = mContext.getDisplayId();
         mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
 
@@ -605,9 +599,8 @@
         mContext.getSystemService(WindowManager.class).removeViewImmediate(
                 mNavigationBarView.getRootView());
         mNavigationModeController.removeListener(this);
-        mAccessibilityButtonModeObserver.removeListener(this);
 
-        mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
+        mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
         mDeviceProvisionedController.removeCallback(mUserSetupListener);
         mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -618,7 +611,8 @@
     @Override
     public void onViewAttachedToWindow(View v) {
         final Display display = v.getDisplay();
-        mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
+        mNavigationBarView.setComponents(mRecentsOptional);
+        mNavigationBarView.setComponents(mStatusBarOptionalLazy.get().get().getPanelController());
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
@@ -629,7 +623,7 @@
         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
         mNavigationBarView.setBehavior(mBehavior);
 
-        mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+        mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
 
         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
         mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -704,6 +698,7 @@
         mHandler.removeCallbacks(mAutoDim);
         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
         mHandler.removeCallbacks(mEnableLayoutTransitions);
+        mFrame = null;
         mNavigationBarView = null;
         mOrientationHandle = null;
     }
@@ -722,6 +717,7 @@
      * Called when a non-reloading configuration change happens and we need to update.
      */
     public void onConfigurationChanged(Configuration newConfig) {
+        final int rotation = newConfig.windowConfiguration.getRotation();
         final Locale locale = mContext.getResources().getConfiguration().locale;
         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
         if (!locale.equals(mLocale) || ld != mLayoutDirection) {
@@ -735,9 +731,8 @@
             refreshLayout(ld);
         }
 
-        repositionNavigationBar();
+        repositionNavigationBar(rotation);
         if (canShowSecondaryHandle()) {
-            int rotation = newConfig.windowConfiguration.getRotation();
             if (rotation != mCurrentRotation) {
                 mCurrentRotation = rotation;
                 orientSecondaryHomeHandle();
@@ -889,30 +884,15 @@
             return;
         }
         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
-        int hints = mNavigationIconHints;
-        switch (backDisposition) {
-            case InputMethodService.BACK_DISPOSITION_DEFAULT:
-            case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
-            case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
-                if (imeShown) {
-                    hints |= NAVIGATION_HINT_BACK_ALT;
-                } else {
-                    hints &= ~NAVIGATION_HINT_BACK_ALT;
-                }
-                break;
-            case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
-                hints &= ~NAVIGATION_HINT_BACK_ALT;
-                break;
-        }
-        if (showImeSwitcher) {
-            hints |= NAVIGATION_HINT_IME_SHOWN;
-        } else {
-            hints &= ~NAVIGATION_HINT_IME_SHOWN;
-        }
+        int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+                imeShown, showImeSwitcher);
         if (hints == mNavigationIconHints) return;
 
         mNavigationIconHints = hints;
-        mNavigationBarView.setNavigationIconHints(hints);
+        if (!isTablet(mContext)) {
+            // All IME functions handled by launcher via Sysui flags for large screen
+            mNavigationBarView.setNavigationIconHints(hints);
+        }
         checkBarModes();
         updateSystemUiStateFlags(-1);
     }
@@ -984,7 +964,7 @@
     @Override
     public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, boolean isFullscreen) {
+            @Behavior int behavior, InsetsState requestedState, String packageName) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -1116,13 +1096,12 @@
                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
     }
 
-    private void repositionNavigationBar() {
-        if (!mNavigationBarView.isAttachedToWindow()) return;
+    private void repositionNavigationBar(int rotation) {
+        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
 
         prepareNavigationBarView();
 
-        mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
-                ((View) mNavigationBarView.getParent()).getLayoutParams());
+        mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation));
     }
 
     private void updateScreenPinningGestures() {
@@ -1164,7 +1143,7 @@
         ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
         accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
-        updateAccessibilityServicesState(mAccessibilityManager);
+        updateAccessibilityServicesState();
 
         ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
         imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1180,13 +1159,14 @@
         // If an incoming call is ringing, HOME is totally disabled.
         // (The user is already on the InCallUI at this point,
         // and their ONLY options are to answer or reject the call.)
+        final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mHomeBlockedThisTouch = false;
                 TelecomManager telecomManager =
                         mContext.getSystemService(TelecomManager.class);
                 if (telecomManager != null && telecomManager.isRinging()) {
-                    if (mStatusBarLazy.get().isKeyguardShowing()) {
+                    if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
                         Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
                                 "No heads up");
                         mHomeBlockedThisTouch = true;
@@ -1202,14 +1182,15 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
-                mStatusBarLazy.get().awakenDreams();
+                statusBarOptional.ifPresent(StatusBar::awakenDreams);
                 break;
         }
         return false;
     }
 
     private void onVerticalChanged(boolean isVertical) {
-        mStatusBarLazy.get().setQsScrimEnabled(!isVertical);
+        mStatusBarOptionalLazy.get().ifPresent(
+                statusBar -> statusBar.setQsScrimEnabled(!isVertical));
     }
 
     private boolean onNavigationTouch(View v, MotionEvent event) {
@@ -1235,7 +1216,7 @@
                 AssistManager.INVOCATION_TYPE_KEY,
                 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
         mAssistManagerLazy.get().startAssist(args);
-        mStatusBarLazy.get().awakenDreams();
+        mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
         mNavigationBarView.abortCurrentGesture();
         return true;
     }
@@ -1261,7 +1242,7 @@
             LatencyTracker.getInstance(mContext).onActionStart(
                     LatencyTracker.ACTION_TOGGLE_RECENTS);
         }
-        mStatusBarLazy.get().awakenDreams();
+        mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
         mCommandQueue.toggleRecentApps();
     }
 
@@ -1366,8 +1347,11 @@
             return false;
         }
 
-        return mStatusBarLazy.get().toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
-                MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
+        return mStatusBarOptionalLazy.get().map(
+                statusBar -> statusBar.toggleSplitScreenMode(
+                        MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+                        MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS))
+            .orElse(false);
     }
 
     private void onAccessibilityClick(View v) {
@@ -1385,9 +1369,8 @@
         return true;
     }
 
-    void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
-        boolean[] feedbackEnabled = new boolean[1];
-        int a11yFlags = getA11yButtonState(feedbackEnabled);
+    void updateAccessibilityServicesState() {
+        int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
 
         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1406,7 +1389,7 @@
 
     public void updateSystemUiStateFlags(int a11yFlags) {
         if (a11yFlags < 0) {
-            a11yFlags = getA11yButtonState(null);
+            a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
         }
         boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1416,6 +1399,8 @@
                 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
                 .setFlag(SYSUI_STATE_IME_SHOWING,
                         (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+                .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+                        (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
                         allowSystemGestureIgnoringBarVisibility())
                 .commitUpdate(mDisplayId);
@@ -1431,45 +1416,6 @@
         }
     }
 
-    /**
-     * Returns the system UI flags corresponding the the current accessibility button state
-     *
-     * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
-     */
-    public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
-        boolean feedbackEnabled = false;
-        // AccessibilityManagerService resolves services for the current user since the local
-        // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
-        final List<AccessibilityServiceInfo> services =
-                mAccessibilityManager.getEnabledAccessibilityServiceList(
-                        AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
-        final List<String> a11yButtonTargets =
-                mAccessibilityManager.getAccessibilityShortcutTargets(
-                        AccessibilityManager.ACCESSIBILITY_BUTTON);
-        final int requestingServices = a11yButtonTargets.size();
-        for (int i = services.size() - 1; i >= 0; --i) {
-            AccessibilityServiceInfo info = services.get(i);
-            if (info.feedbackType != 0 && info.feedbackType !=
-                    AccessibilityServiceInfo.FEEDBACK_GENERIC) {
-                feedbackEnabled = true;
-            }
-        }
-
-        if (outFeedbackEnabled != null) {
-            outFeedbackEnabled[0] = feedbackEnabled;
-        }
-
-        // If accessibility button is floating menu mode, click and long click state should be
-        // disabled.
-        if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
-                == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
-            return 0;
-        }
-
-        return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
-                | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
-    }
-
     private void updateAssistantEntrypoints() {
         mAssistantAvailable = mAssistManagerLazy.get()
                 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
@@ -1531,7 +1477,7 @@
     private void checkBarModes() {
         // We only have status bar on default display now.
         if (mIsOnDefaultDisplay) {
-            mStatusBarLazy.get().checkBarModes();
+            mStatusBarOptionalLazy.get().ifPresent(StatusBar::checkBarModes);
         } else {
             checkNavBarModes();
         }
@@ -1549,7 +1495,8 @@
      * Checks current navigation bar mode and make transitions.
      */
     public void checkNavBarModes() {
-        final boolean anim = mStatusBarLazy.get().isDeviceInteractive()
+        final boolean anim =
+                mStatusBarOptionalLazy.get().map(StatusBar::isDeviceInteractive).orElse(false)
                 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
         mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
     }
@@ -1571,11 +1518,6 @@
         }
     }
 
-    @Override
-    public void onAccessibilityButtonModeChanged(int mode) {
-        updateAccessibilityServicesState(mAccessibilityManager);
-    }
-
     public void disableAnimationsDuringHide(long delay) {
         mNavigationBarView.setLayoutTransitionsEnabled(false);
         mHandler.postDelayed(mEnableLayoutTransitions,
@@ -1600,16 +1542,97 @@
         mNavigationBarView.getBarTransitions().finishAnimations();
     }
 
-    private final AccessibilityServicesStateChangeListener mAccessibilityListener =
+    private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
             this::updateAccessibilityServicesState;
 
+    private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+        WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+        lp.paramsForRotation = new WindowManager.LayoutParams[4];
+        for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+            lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+        }
+        return lp;
+    }
+
+    private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+        int width = WindowManager.LayoutParams.MATCH_PARENT;
+        int height = WindowManager.LayoutParams.MATCH_PARENT;
+        int insetsHeight = -1;
+        int gravity = Gravity.BOTTOM;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            boolean navBarCanMove = true;
+            if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
+                Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
+                navBarCanMove = displaySize.width() != displaySize.height()
+                        && mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_navBarCanMove);
+            }
+            if (!navBarCanMove) {
+                height = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_frame_height);
+                insetsHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_height);
+            } else {
+                switch (rotation) {
+                    case ROTATION_UNDEFINED:
+                    case Surface.ROTATION_0:
+                    case Surface.ROTATION_180:
+                        height = mContext.getResources().getDimensionPixelSize(
+                                com.android.internal.R.dimen.navigation_bar_frame_height);
+                        insetsHeight = mContext.getResources().getDimensionPixelSize(
+                                com.android.internal.R.dimen.navigation_bar_height);
+                        break;
+                    case Surface.ROTATION_90:
+                        gravity = Gravity.RIGHT;
+                        width = mContext.getResources().getDimensionPixelSize(
+                                com.android.internal.R.dimen.navigation_bar_width);
+                        break;
+                    case Surface.ROTATION_270:
+                        gravity = Gravity.LEFT;
+                        width = mContext.getResources().getDimensionPixelSize(
+                                com.android.internal.R.dimen.navigation_bar_width);
+                        break;
+                }
+            }
+        }
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                width,
+                height,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
+                PixelFormat.TRANSLUCENT);
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            lp.gravity = gravity;
+            if (insetsHeight != -1) {
+                lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
+            } else {
+                lp.providedInternalInsets = Insets.NONE;
+            }
+        }
+        lp.token = new Binder();
+        lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
+        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        lp.windowAnimations = 0;
+        lp.setTitle("NavigationBar" + mContext.getDisplayId());
+        lp.setFitInsetsTypes(0 /* types */);
+        lp.setTrustedOverlay();
+        return lp;
+    }
+
     private boolean canShowSecondaryHandle() {
         return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
     }
 
     private final Consumer<Integer> mRotationWatcher = rotation -> {
-        if (mNavigationBarView.needsReorient(rotation)) {
-            repositionNavigationBar();
+        if (mNavigationBarView != null
+                && mNavigationBarView.needsReorient(rotation)) {
+            repositionNavigationBar(rotation);
         }
     };
 
@@ -1629,7 +1652,7 @@
             }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 // The accessibility settings may be different for the new user
-                updateAccessibilityServicesState(mAccessibilityManager);
+                updateAccessibilityServicesState();
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
new file mode 100644
index 0000000..13e6d8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
@@ -0,0 +1,90 @@
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Extracts shared elements of a11y necessary between navbar and taskbar delegate
+ */
+@SysUISingleton
+public final class NavigationBarA11yHelper implements
+        AccessibilityButtonModeObserver.ModeChangedListener {
+    private final AccessibilityManager mAccessibilityManager;
+    private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+    private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
+
+    @Inject
+    public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
+        mAccessibilityManager = accessibilityManager;
+        accessibilityManagerWrapper.addCallback(
+                accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
+        mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+        mAccessibilityButtonModeObserver.addListener(this);
+    }
+
+    public void registerA11yEventListener(NavA11yEventListener listener) {
+        mA11yEventListeners.add(listener);
+    }
+
+    public void removeA11yEventListener(NavA11yEventListener listener) {
+        mA11yEventListeners.remove(listener);
+    }
+
+    private void dispatchEventUpdate() {
+        for (NavA11yEventListener listener : mA11yEventListeners) {
+            listener.updateAccessibilityServicesState();
+        }
+    }
+
+    @Override
+    public void onAccessibilityButtonModeChanged(int mode) {
+        dispatchEventUpdate();
+    }
+
+    /**
+     * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+     * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+     *
+     * @return the a11y button clickable and long_clickable states, or 0 if there is no
+     *         a11y button in the navbar
+     */
+    public int getA11yButtonState() {
+        // AccessibilityManagerService resolves services for the current user since the local
+        // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+        final List<String> a11yButtonTargets =
+                mAccessibilityManager.getAccessibilityShortcutTargets(
+                        AccessibilityManager.ACCESSIBILITY_BUTTON);
+        final int requestingServices = a11yButtonTargets.size();
+
+        // If accessibility button is floating menu mode, click and long click state should be
+        // disabled.
+        if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+                == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+            return 0;
+        }
+
+        return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+                | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+    }
+
+    public interface NavA11yEventListener {
+        void updateAccessibilityServicesState();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b9e9240..458c50d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,16 +19,15 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
@@ -86,8 +85,6 @@
         ConfigurationController.ConfigurationListener,
         NavigationModeController.ModeChangedListener, Dumpable {
 
-    private static final float TABLET_MIN_DPS = 600;
-
     private static final String TAG = NavigationBarController.class.getSimpleName();
 
     private final Context mContext;
@@ -107,12 +104,13 @@
     private final Optional<Pip> mPipOptional;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final Optional<Recents> mRecentsOptional;
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final ShadeController mShadeController;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final SystemActions mSystemActions;
     private final UiEventLogger mUiEventLogger;
     private final Handler mHandler;
+    private final NavigationBarA11yHelper mNavigationBarA11yHelper;
     private final DisplayManager mDisplayManager;
     private final NavigationBarOverlayController mNavBarOverlayController;
     private final TaskbarDelegate mTaskbarDelegate;
@@ -148,7 +146,7 @@
             Optional<Pip> pipOptional,
             Optional<LegacySplitScreen> splitScreenOptional,
             Optional<Recents> recentsOptional,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             ShadeController shadeController,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationShadeDepthController notificationShadeDepthController,
@@ -157,6 +155,8 @@
             UiEventLogger uiEventLogger,
             NavigationBarOverlayController navBarOverlayController,
             ConfigurationController configurationController,
+            NavigationBarA11yHelper navigationBarA11yHelper,
+            TaskbarDelegate taskbarDelegate,
             UserTracker userTracker) {
         mContext = context;
         mWindowManager = windowManager;
@@ -175,13 +175,14 @@
         mPipOptional = pipOptional;
         mSplitScreenOptional = splitScreenOptional;
         mRecentsOptional = recentsOptional;
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mShadeController = shadeController;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mNotificationShadeDepthController = notificationShadeDepthController;
         mSystemActions = systemActions;
         mUiEventLogger = uiEventLogger;
         mHandler = mainHandler;
+        mNavigationBarA11yHelper = navigationBarA11yHelper;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         commandQueue.addCallback(this);
         configurationController.addCallback(this);
@@ -189,15 +190,17 @@
         mNavBarOverlayController = navBarOverlayController;
         mNavMode = mNavigationModeController.addListener(this);
         mNavigationModeController.addListener(this);
-        mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
-        mIsTablet = isTablet(mContext.getResources().getConfiguration());
+        mTaskbarDelegate = taskbarDelegate;
+        mTaskbarDelegate.setOverviewProxyService(overviewProxyService,
+                navigationBarA11yHelper, mSysUiFlagsContainer);
+        mIsTablet = isTablet(mContext);
         mUserTracker = userTracker;
     }
 
     @Override
     public void onConfigChanged(Configuration newConfig) {
         boolean isOldConfigTablet = mIsTablet;
-        mIsTablet = isTablet(newConfig);
+        mIsTablet = isTablet(newConfig, mContext);
         boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
         // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
         if (largeScreenChanged && updateNavbarForTaskbar()) {
@@ -237,25 +240,28 @@
         });
     }
 
-    /**
-     * @return {@code true} if navbar was added/removed, false otherwise
-     */
-    public boolean updateNavbarForTaskbar() {
-        if (!isThreeButtonTaskbarFlagEnabled()) {
-            return false;
+    /** @see #initializeTaskbarIfNecessary() */
+    private boolean updateNavbarForTaskbar() {
+        boolean taskbarShown = initializeTaskbarIfNecessary();
+        if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+            createNavigationBar(mContext.getDisplay(), null, null);
         }
+        return taskbarShown;
+    }
 
-        if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
+    /** @return {@code true} if taskbar is enabled, false otherwise */
+    private boolean initializeTaskbarIfNecessary() {
+        boolean isShowingTaskbar = mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON;
+        if (isShowingTaskbar) {
             // Remove navigation bar when taskbar is showing, currently only for 3 button mode
             removeNavigationBar(mContext.getDisplayId());
             mCommandQueue.addCallback(mTaskbarDelegate);
-        } else if (mNavigationBars.get(mContext.getDisplayId()) == null) {
-            // Add navigation bar after taskbar goes away
-            createNavigationBar(mContext.getDisplay(), null, null);
+            mTaskbarDelegate.init(mContext.getDisplayId());
+        } else {
             mCommandQueue.removeCallback(mTaskbarDelegate);
+            mTaskbarDelegate.destroy();
         }
-
-        return true;
+        return isShowingTaskbar;
     }
 
     @Override
@@ -266,7 +272,7 @@
     @Override
     public void onDisplayReady(int displayId) {
         Display display = mDisplayManager.getDisplay(displayId);
-        mIsTablet = isTablet(mContext.getResources().getConfiguration());
+        mIsTablet = isTablet(mContext);
         createNavigationBar(display, null /* savedState */, null /* result */);
     }
 
@@ -302,7 +308,7 @@
      */
     public void createNavigationBars(final boolean includeDefaultDisplay,
             RegisterStatusBarResult result) {
-        if (updateNavbarForTaskbar()) {
+        if (initializeTaskbarIfNecessary()) {
             return;
         }
 
@@ -326,7 +332,7 @@
             return;
         }
 
-        if (isThreeButtonTaskbarEnabled()) {
+        if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
             return;
         }
 
@@ -363,7 +369,7 @@
                 mPipOptional,
                 mSplitScreenOptional,
                 mRecentsOptional,
-                mStatusBarLazy,
+                mStatusBarOptionalLazy,
                 mShadeController,
                 mNotificationRemoteInputManager,
                 mNotificationShadeDepthController,
@@ -371,6 +377,7 @@
                 mHandler,
                 mNavBarOverlayController,
                 mUiEventLogger,
+                mNavigationBarA11yHelper,
                 mUserTracker);
         mNavigationBars.put(displayId, navBar);
 
@@ -462,24 +469,6 @@
         return mNavigationBars.get(DEFAULT_DISPLAY);
     }
 
-    private boolean isThreeButtonTaskbarEnabled() {
-        return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON &&
-                isThreeButtonTaskbarFlagEnabled();
-    }
-
-    private boolean isThreeButtonTaskbarFlagEnabled() {
-        return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
-    }
-
-    private boolean isTablet(Configuration newConfig) {
-        float density = Resources.getSystem().getDisplayMetrics().density;
-        int size = Math.min((int) (density * newConfig.screenWidthDp),
-                (int) (density* newConfig.screenHeightDp));
-        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
-        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-        return (size / densityRatio) >= TABLET_MIN_DPS;
-    }
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         for (int i = 0; i < mNavigationBars.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 61e8033..808b7e2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -69,6 +69,7 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
 import com.android.systemui.navigationbar.buttons.ContextualButton;
 import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
@@ -96,6 +97,7 @@
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Consumer;
 
 public class NavigationBarView extends FrameLayout implements
@@ -162,6 +164,7 @@
     private Configuration mTmpLastConfiguration;
 
     private NavigationBarInflaterView mNavigationInflaterView;
+    private Optional<Recents> mRecentsOptional = Optional.empty();
     private NotificationPanelViewController mPanelView;
     private RotationContextButton mRotationContextButton;
     private FloatingRotationButton mFloatingRotationButton;
@@ -250,7 +253,7 @@
                 @Override
                 public boolean performAccessibilityAction(View host, int action, Bundle args) {
                     if (action == R.id.action_toggle_overview) {
-                        Dependency.get(Recents.class).toggleRecentApps();
+                        mRecentsOptional.ifPresent(Recents::toggleRecentApps);
                     } else {
                         return super.performAccessibilityAction(host, action, args);
                     }
@@ -273,14 +276,23 @@
                 false /* inScreen */, false /* useNearestRegion */));
     };
 
-    private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
-        if (visible) {
-            // If the button will actually become visible and the navbar is about to hide,
-            // tell the statusbar to keep it around for longer
-            mAutoHideController.touchAutoHide();
-        }
-        notifyActiveTouchRegions();
-    };
+    private final RotationButtonUpdatesCallback mRotationButtonListener =
+            new RotationButtonUpdatesCallback() {
+                @Override
+                public void onVisibilityChanged(boolean visible) {
+                    if (visible) {
+                        // If the button will actually become visible and the navbar is about
+                        // to hide, tell the statusbar to keep it around for longer
+                        mAutoHideController.touchAutoHide();
+                    }
+                    notifyActiveTouchRegions();
+                }
+
+                @Override
+                public void onPositionChanged() {
+                    notifyActiveTouchRegions();
+                }
+            };
 
     private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
         if (visible) {
@@ -385,6 +397,10 @@
         return mBarTransitions.getLightTransitionsController();
     }
 
+    public void setComponents(Optional<Recents> recentsOptional) {
+        mRecentsOptional = recentsOptional;
+    }
+
     public void setComponents(NotificationPanelViewController panel) {
         mPanelView = panel;
         updatePanelSystemUiStateFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
index e487858..3486c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
@@ -20,12 +20,10 @@
 
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
 
-import java.util.function.Consumer;
-
 /** Interface of a rotation button that interacts {@link RotationButtonController}. */
 public interface RotationButton {
     void setRotationButtonController(RotationButtonController rotationButtonController);
-    void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback);
+    void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback);
     View getCurrentView();
     boolean show();
     boolean hide();
@@ -39,4 +37,12 @@
     default boolean acceptRotationProposal() {
         return getCurrentView() != null;
     }
+
+    /**
+     * Callback for updates provided by a rotation button
+     */
+    interface RotationButtonUpdatesCallback {
+        void onVisibilityChanged(boolean isVisible);
+        void onPositionChanged();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 649ac87..196625b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -46,7 +46,10 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
+import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.utilities.ViewRippler;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -138,12 +141,12 @@
     }
 
     void setRotationButton(RotationButton rotationButton,
-            Consumer<Boolean> visibilityChangedCallback) {
+                           RotationButtonUpdatesCallback updatesCallback) {
         mRotationButton = rotationButton;
         mRotationButton.setRotationButtonController(this);
         mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
         mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
-        mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
+        mRotationButton.setUpdatesCallback(updatesCallback);
     }
 
     void registerListeners() {
@@ -311,7 +314,7 @@
 
         // Prepare to show the navbar icon by updating the icon style to change anim params
         mLastRotationSuggestion = rotation; // Remember rotation for click
-        final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
+        final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
         if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
             mIconResId = rotationCCW
                     ? R.drawable.ic_sysbar_rotate_button_ccw_start_90
@@ -431,23 +434,6 @@
         return rotation == NATURAL_ROTATION;
     }
 
-    private boolean isRotationAnimationCCW(int from, int to) {
-        // All 180deg WM rotation animations are CCW, match that
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
-        return false; // Default
-    }
-
     private void rescheduleRotationTimeout(final boolean reasonHover) {
         // May be called due to a new rotation proposal or a change in hover state
         if (reasonHover) {
@@ -520,38 +506,6 @@
         }
     }
 
-    private class ViewRippler {
-        private static final int RIPPLE_OFFSET_MS = 50;
-        private static final int RIPPLE_INTERVAL_MS = 2000;
-        private View mRoot;
-
-        public void start(View root) {
-            stop(); // Stop any pending ripple animations
-
-            mRoot = root;
-
-            // Schedule pending ripples, offset the 1st to avoid problems with visibility change
-            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
-            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
-        }
-
-        public void stop() {
-            if (mRoot != null) mRoot.removeCallbacks(mRipple);
-        }
-
-        private final Runnable mRipple = new Runnable() {
-            @Override
-            public void run() { // Cause the ripple to fire via false presses
-                if (!mRoot.isAttachedToWindow()) return;
-                mRoot.setPressed(true /* pressed */);
-                mRoot.setPressed(false /* pressed */);
-            }
-        };
-    }
-
     enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
         @UiEvent(doc = "The rotation button was shown")
         ROTATION_SUGGESTION_SHOWN(206),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 03147d8..1d44146 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,23 +16,100 @@
 
 package com.android.systemui.navigationbar;
 
-import android.os.IBinder;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
 
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+import android.view.InsetsState;
+
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.statusbar.CommandQueue;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
 public class TaskbarDelegate implements CommandQueue.Callbacks {
 
-    private final OverviewProxyService mOverviewProxyService;
+    private OverviewProxyService mOverviewProxyService;
+    private NavigationBarA11yHelper mNavigationBarA11yHelper;
+    private SysUiState mSysUiState;
+    private int mDisplayId;
+    private int mNavigationIconHints;
+    private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
+            this::updateSysuiFlags;
+    @Inject
+    public TaskbarDelegate() { /* no-op */ }
 
-    public TaskbarDelegate(OverviewProxyService overviewProxyService) {
+    public void setOverviewProxyService(OverviewProxyService overviewProxyService,
+            NavigationBarA11yHelper navigationBarA11yHelper,
+            SysUiState sysUiState) {
+        // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mOverviewProxyService = overviewProxyService;
+        mNavigationBarA11yHelper = navigationBarA11yHelper;
+        mSysUiState = sysUiState;
+    }
+
+    public void destroy() {
+        mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+    }
+
+    public void init(int displayId) {
+        mDisplayId = displayId;
+        mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+        // Set initial state for any listeners
+        updateSysuiFlags();
+    }
+
+    private void updateSysuiFlags() {
+        int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+        boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+
+        mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
+                .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
+                .setFlag(SYSUI_STATE_IME_SHOWING,
+                        (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+                .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+                        (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+                .commitUpdate(mDisplayId);
     }
 
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
-        mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition,
-                showImeSwitcher);
+        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+        int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+                imeShown, showImeSwitcher);
+        if (hints != mNavigationIconHints) {
+            mNavigationIconHints = hints;
+            updateSysuiFlags();
+        }
+    }
+
+    @Override
+    public void onRotationProposal(int rotation, boolean isValid) {
+        mOverviewProxyService.onRotationProposal(rotation, isValid);
+    }
+
+    @Override
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        mOverviewProxyService.disable(displayId, state1, state2, animate);
+    }
+
+    @Override
+    public void onSystemBarAttributesChanged(int displayId, int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
+            InsetsState requestedState, String packageName) {
+        mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
index 6a97a33..ebb67af 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
@@ -23,10 +23,6 @@
 
 import com.android.systemui.navigationbar.RotationButton;
 import com.android.systemui.navigationbar.RotationButtonController;
-import com.android.systemui.navigationbar.buttons.ContextualButton;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-
-import java.util.function.Consumer;
 
 /** Containing logic for the rotation button in nav bar. */
 public class RotationContextButton extends ContextualButton implements RotationButton {
@@ -48,13 +44,10 @@
     }
 
     @Override
-    public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
-        setListener(new ContextButtonListener() {
-            @Override
-            public void onVisibilityChanged(ContextualButton button, boolean visible) {
-                if (visibilityChangedCallback != null) {
-                    visibilityChangedCallback.accept(visible);
-                }
+    public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+        setListener((button, visible) -> {
+            if (updatesCallback != null) {
+                updatesCallback.onVisibilityChanged(visible);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
index 61118c5..4605795 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
@@ -20,48 +20,72 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
-import android.view.Gravity;
 import android.view.LayoutInflater;
-import android.view.Surface;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.navigationbar.RotationButton;
 import com.android.systemui.navigationbar.RotationButtonController;
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
 import com.android.systemui.navigationbar.buttons.KeyButtonView;
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position;
 
-import java.util.function.Consumer;
-
-/** Containing logic for the rotation button on the physical left bottom corner of the screen. */
+/**
+ * Containing logic for the rotation button on the physical left bottom corner of the screen.
+ */
 public class FloatingRotationButton implements RotationButton {
 
     private static final float BACKGROUND_ALPHA = 0.92f;
+    private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
 
-    private final Context mContext;
     private final WindowManager mWindowManager;
+    private final ViewGroup mKeyButtonContainer;
     private final KeyButtonView mKeyButtonView;
-    private final int mDiameter;
-    private final int mMargin;
+
+    private final int mContainerSize;
+
     private KeyButtonDrawable mKeyButtonDrawable;
     private boolean mIsShowing;
     private boolean mCanShow = true;
+    private int mDisplayRotation;
+
+    private boolean mIsTaskbarVisible = false;
+    private boolean mIsTaskbarStashed = false;
+
+    private final FloatingRotationButtonPositionCalculator mPositionCalculator;
 
     private RotationButtonController mRotationButtonController;
-    private Consumer<Boolean> mVisibilityChangedCallback;
+    private RotationButtonUpdatesCallback mUpdatesCallback;
+    private Position mPosition;
 
     public FloatingRotationButton(Context context) {
-        mContext = context;
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate(
+        mWindowManager = context.getSystemService(WindowManager.class);
+        mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(
                 R.layout.rotate_suggestion, null);
+        mKeyButtonView = mKeyButtonContainer.findViewById(R.id.rotate_suggestion);
         mKeyButtonView.setVisibility(View.VISIBLE);
 
-        Resources res = mContext.getResources();
-        mDiameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
-        mMargin = Math.max(res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
+        Resources res = context.getResources();
+
+        int defaultMargin = Math.max(
+                res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
                 res.getDimensionPixelSize(R.dimen.rounded_corner_content_padding));
+
+        int taskbarMarginLeft =
+                res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_left_margin);
+        int taskbarMarginBottom =
+                res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_bottom_margin);
+
+        mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
+                taskbarMarginLeft, taskbarMarginBottom);
+
+        final int diameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
+        mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
+                taskbarMarginBottom));
     }
 
     @Override
@@ -72,8 +96,8 @@
     }
 
     @Override
-    public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
-        mVisibilityChangedCallback = visibilityChangedCallback;
+    public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+        mUpdatesCallback = updatesCallback;
     }
 
     @Override
@@ -86,45 +110,39 @@
         if (!mCanShow || mIsShowing) {
             return false;
         }
+
         mIsShowing = true;
         int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
-                mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                mContainerSize,
+                mContainerSize,
+                0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
                 PixelFormat.TRANSLUCENT);
+
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("FloatingRotationButton");
         lp.setFitInsetsTypes(0 /*types */);
-        switch (mWindowManager.getDefaultDisplay().getRotation()) {
-            case Surface.ROTATION_0:
-                lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
-                break;
-            case Surface.ROTATION_90:
-                lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
-                break;
-            case Surface.ROTATION_180:
-                lp.gravity = Gravity.TOP | Gravity.RIGHT;
-                break;
-            case Surface.ROTATION_270:
-                lp.gravity = Gravity.TOP | Gravity.LEFT;
-                break;
-            default:
-                break;
-        }
-        mWindowManager.addView(mKeyButtonView, lp);
+
+        mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+        mPosition = mPositionCalculator
+                .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+        lp.gravity = mPosition.getGravity();
+        ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+                mPosition.getGravity();
+
+        updateTranslation(mPosition, /* animate */ false);
+
+        mWindowManager.addView(mKeyButtonContainer, lp);
         if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
             mKeyButtonDrawable.resetAnimation();
             mKeyButtonDrawable.startAnimation();
         }
-        mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5,
-                    int i6, int i7) {
-                if (mIsShowing && mVisibilityChangedCallback != null) {
-                    mVisibilityChangedCallback.accept(true);
-                }
-                mKeyButtonView.removeOnLayoutChangeListener(this);
-            }
-        });
+
+        if (mUpdatesCallback != null) {
+            mUpdatesCallback.onVisibilityChanged(true);
+        }
+
         return true;
     }
 
@@ -133,10 +151,10 @@
         if (!mIsShowing) {
             return false;
         }
-        mWindowManager.removeViewImmediate(mKeyButtonView);
+        mWindowManager.removeViewImmediate(mKeyButtonContainer);
         mIsShowing = false;
-        if (mVisibilityChangedCallback != null) {
-            mVisibilityChangedCallback.accept(false);
+        if (mUpdatesCallback != null) {
+            mUpdatesCallback.onVisibilityChanged(false);
         }
         return true;
     }
@@ -183,4 +201,43 @@
             hide();
         }
     }
+
+    public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
+        mIsTaskbarVisible = taskbarVisible;
+        mIsTaskbarStashed = taskbarStashed;
+
+        if (!mIsShowing) return;
+
+        final Position newPosition = mPositionCalculator
+                .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+        if (newPosition.getTranslationX() != mPosition.getTranslationX()
+                || newPosition.getTranslationY() != mPosition.getTranslationY()) {
+            updateTranslation(newPosition, /* animate */ true);
+            mPosition = newPosition;
+        }
+    }
+
+    private void updateTranslation(Position position, boolean animate) {
+        final int translationX = position.getTranslationX();
+        final int translationY = position.getTranslationY();
+
+        if (animate) {
+            mKeyButtonView
+                    .animate()
+                    .translationX(translationX)
+                    .translationY(translationY)
+                    .setDuration(MARGIN_ANIMATION_DURATION_MILLIS)
+                    .setInterpolator(new AccelerateDecelerateInterpolator())
+                    .withEndAction(() -> {
+                        if (mUpdatesCallback != null && mIsShowing) {
+                            mUpdatesCallback.onPositionChanged();
+                        }
+                    })
+                    .start();
+        } else {
+            mKeyButtonView.setTranslationX(translationX);
+            mKeyButtonView.setTranslationY(translationY);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
new file mode 100644
index 0000000..3ce51ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
@@ -0,0 +1,65 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+
+/**
+ * Calculates gravity and translation that is necessary to display
+ * the button in the correct position based on the current state
+ */
+internal class FloatingRotationButtonPositionCalculator(
+    private val defaultMargin: Int,
+    private val taskbarMarginLeft: Int,
+    private val taskbarMarginBottom: Int
+) {
+
+    fun calculatePosition(
+        currentRotation: Int,
+        taskbarVisible: Boolean,
+        taskbarStashed: Boolean
+    ): Position {
+
+        val isTaskbarSide = currentRotation == Surface.ROTATION_0
+            || currentRotation == Surface.ROTATION_90
+        val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed
+
+        val gravity = resolveGravity(currentRotation)
+
+        val marginLeft = if (useTaskbarMargin) taskbarMarginLeft else defaultMargin
+        val marginBottom = if (useTaskbarMargin) taskbarMarginBottom else defaultMargin
+
+        val translationX =
+            if (gravity and Gravity.RIGHT == Gravity.RIGHT) {
+                -marginLeft
+            } else {
+                marginLeft
+            }
+        val translationY =
+            if (gravity and Gravity.BOTTOM == Gravity.BOTTOM) {
+                -marginBottom
+            } else {
+                marginBottom
+            }
+
+        return Position(
+            gravity = gravity,
+            translationX = translationX,
+            translationY = translationY
+        )
+    }
+
+    data class Position(
+        val gravity: Int,
+        val translationX: Int,
+        val translationY: Int
+    )
+
+    private fun resolveGravity(rotation: Int): Int =
+        when (rotation) {
+            Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
+            Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
+            Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
+            Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
+            else -> throw IllegalArgumentException("Invalid rotation $rotation")
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 9859034..82a5aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -450,7 +450,7 @@
             PeopleTileKey key = new PeopleTileKey(
                     sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName());
             if (!PeopleTileKey.isValid(key)) {
-                Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
+                if (DEBUG) Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
                 return;
             }
             int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index a888305..6252654 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -54,6 +54,7 @@
 import java.io.PrintWriter;
 import java.time.Duration;
 import java.util.Arrays;
+import java.util.Optional;
 import java.util.concurrent.Future;
 
 import javax.inject.Inject;
@@ -108,15 +109,15 @@
     private IThermalEventListener mUsbThermalEventListener;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final CommandQueue mCommandQueue;
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
 
     @Inject
     public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
-            CommandQueue commandQueue, Lazy<StatusBar> statusBarLazy) {
+            CommandQueue commandQueue, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
         super(context);
         mBroadcastDispatcher = broadcastDispatcher;
         mCommandQueue = commandQueue;
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
     }
 
     public void start() {
@@ -710,7 +711,8 @@
             int status = temp.getStatus();
 
             if (status >= Temperature.THROTTLING_EMERGENCY) {
-                if (!mStatusBarLazy.get().isDeviceInVrMode()) {
+                final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+                if (!statusBarOptional.map(StatusBar::isDeviceInVrMode).orElse(false)) {
                     mWarnings.showHighTemperatureWarning();
                     Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
                             + ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e747..eec69f98 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
         appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
                 .toList()
                 .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
-                        { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+                        { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
         types = itemsList.map { it.privacyType }.distinct().sorted()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e9b19e5..adcaf5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -27,6 +27,7 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
@@ -47,6 +48,7 @@
     private float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private NonInterceptingScrollView mQSPanelContainer;
+    private ImageView mDragHandle;
 
     private int mSideMargins;
     private boolean mQsDisabled;
@@ -65,6 +67,7 @@
         mQSDetail = findViewById(R.id.qs_detail);
         mHeader = findViewById(R.id.header);
         mQSCustomizer = findViewById(R.id.qs_customize);
+        mDragHandle = findViewById(R.id.qs_drag_handle);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
     }
 
@@ -160,8 +163,8 @@
             QuickStatusBarHeaderController quickStatusBarHeaderController) {
         mQSPanelContainer.setPaddingRelative(
                 getPaddingStart(),
-                mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height),
+                mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height),
                 getPaddingEnd(),
                 getPaddingBottom()
         );
@@ -195,6 +198,8 @@
         mQSDetail.setBottom(getTop() + scrollBottom);
         int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
         mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
+        // Pin the drag handle to the bottom of the panel.
+        mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
     }
 
     protected int calculateContainerHeight() {
@@ -216,6 +221,7 @@
     public void setExpansion(float expansion) {
         mQsExpansion = expansion;
         mQSPanelContainer.setScrollingEnabled(expansion > 0f);
+        mDragHandle.setAlpha(1.0f - expansion);
         updateExpansion();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 0a1e9d0..d56fe48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -32,6 +32,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -45,7 +46,6 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -86,6 +86,7 @@
     private QSFooter mFooter;
     private float mLastQSExpansion = -1;
     private boolean mQsDisabled;
+    private ImageView mQsDragHandler;
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final InjectionInflationController mInjectionInflater;
@@ -111,7 +112,6 @@
     private QuickQSPanelController mQuickQSPanelController;
     private QSCustomizerController mQSCustomizerController;
     private ScrollListener mScrollListener;
-    private FeatureFlags mFeatureFlags;
     /**
      * When true, QS will translate from outside the screen. It will be clipped with parallax
      * otherwise.
@@ -135,7 +135,7 @@
             StatusBarStateController statusBarStateController, CommandQueue commandQueue,
             QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
-            QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags,
+            QSFragmentComponent.Factory qsComponentFactory,
             FalsingManager falsingManager) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
@@ -146,7 +146,6 @@
         mQsComponentFactory = qsComponentFactory;
         commandQueue.observe(getLifecycle(), this);
         mHost = qsTileHost;
-        mFeatureFlags = featureFlags;
         mFalsingManager = falsingManager;
         mStatusBarStateController = statusBarStateController;
     }
@@ -187,6 +186,7 @@
         mHeader = view.findViewById(R.id.header);
         mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
         mFooter = qsFragmentComponent.getQSFooter();
+        mQsDragHandler = view.findViewById(R.id.qs_drag_handle);
 
         mQsDetailDisplayer.setQsPanelController(mQSPanelController);
 
@@ -291,6 +291,7 @@
                 mQSAnimator.onRtlChanged();
             }
         }
+        updateQsState();
     }
 
     @Override
@@ -363,15 +364,19 @@
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        mFooter.setVisibility(
-                !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
-                        || mShowCollapsedOnKeyguard)
+        mFooter.setVisibility(!mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
+                || mShowCollapsedOnKeyguard)
                 ? View.VISIBLE
                 : View.INVISIBLE);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (mQsExpanded && !mStackScrollerOverscrolling));
         mQSPanelController.setVisibility(
                 !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+        mQsDragHandler.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
+                || mShowCollapsedOnKeyguard)
+                && Utils.shouldUseSplitNotificationShade(getResources())
+                ? View.VISIBLE
+                : View.GONE);
     }
 
     private boolean isKeyguardState() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7c7f566..0c65510 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -42,7 +42,6 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.animation.UniqueObjectHostView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -56,6 +55,8 @@
     private static final String TAG = "QSPanel";
 
     protected final Context mContext;
+    private final int mMediaTopMargin;
+    private final int mMediaTotalBottomMargin;
 
     /**
      * The index where the content starts that needs to be moved between parents
@@ -99,13 +100,14 @@
     protected LinearLayout mHorizontalContentContainer;
 
     protected QSTileLayout mTileLayout;
-    private int mMediaTotalBottomMargin;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUsingMediaPlayer = useQsMediaPlayer(context);
         mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.quick_settings_bottom_margin_media);
+        mMediaTopMargin = getResources().getDimensionPixelSize(
+                R.dimen.qs_tile_margin_vertical);
         mContext = context;
 
         setOrientation(VERTICAL);
@@ -316,7 +318,6 @@
         super.onConfigurationChanged(newConfig);
         mOnConfigurationChangedListeners.forEach(
                 listener -> listener.onConfigurationChange(newConfig));
-        switchSecurityFooter();
     }
 
     @Override
@@ -328,7 +329,7 @@
     private void updateHorizontalLinearLayoutMargins() {
         if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
             LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
-            lp.bottomMargin = mMediaTotalBottomMargin - getPaddingBottom();
+            lp.bottomMargin = Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0);
             mHorizontalLinearLayout.setLayoutParams(lp);
         }
     }
@@ -343,6 +344,13 @@
         return true;
     }
 
+    /**
+     * @return true if the media view needs margin on the top to separate it from the qs tiles
+     */
+    protected boolean mediaNeedsTopMargin() {
+        return false;
+    }
+
     private boolean needsDynamicRowsAndColumns() {
         return true;
     }
@@ -359,25 +367,21 @@
             switchToParent(mFooter, parent, index);
             index++;
         }
-
-        // The security footer is switched on orientation changes
     }
 
-    private void switchSecurityFooter() {
-        if (mSecurityFooter != null) {
-            if (mContext.getResources().getConfiguration().orientation
-                    == Configuration.ORIENTATION_LANDSCAPE && mHeaderContainer != null) {
-                // Adding the security view to the header, that enables us to avoid scrolling
-                switchToParent(mSecurityFooter, mHeaderContainer, 0);
-            } else {
-                // Where should this go? If there's media, right before it. Otherwise, at the end.
-                View mediaView = findViewByPredicate(v -> v instanceof UniqueObjectHostView);
-                int index = -1;
-                if (mediaView != null) {
-                    index = indexOfChild(mediaView);
-                }
-                switchToParent(mSecurityFooter, this, index);
-            }
+    /** Switch the security footer between top and bottom of QS depending on orientation. */
+    public void switchSecurityFooter(boolean shouldUseSplitNotificationShade) {
+        if (mSecurityFooter == null) return;
+
+        if (!shouldUseSplitNotificationShade
+                && mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE && mHeaderContainer != null) {
+            // Adding the security view to the header, that enables us to avoid scrolling
+            switchToParent(mSecurityFooter, mHeaderContainer, 0);
+        } else {
+            // Add after the footer
+            int index = indexOfChild(mFooter);
+            switchToParent(mSecurityFooter, this, index + 1);
         }
     }
 
@@ -411,7 +415,9 @@
             // necessary if the view isn't horizontal, since otherwise the padding is
             // carried in the parent of this view (to ensure correct vertical alignment)
             layoutParams.bottomMargin = !horizontal || displayMediaMarginsOnMedia()
-                    ? mMediaTotalBottomMargin - getPaddingBottom() : 0;
+                    ? Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0) : 0;
+            layoutParams.topMargin = mediaNeedsTopMargin() && !horizontal
+                    ? mMediaTopMargin : 0;
         }
     }
 
@@ -652,9 +658,14 @@
         return mListening;
     }
 
-    public void setSecurityFooter(View view) {
+    /**
+     * Set the security footer view and switch it into the right place
+     * @param view the view in question
+     * @param shouldUseSplitNotificationShade if QS is in split shade mode
+     */
+    public void setSecurityFooter(View view, boolean shouldUseSplitNotificationShade) {
         mSecurityFooter = view;
-        switchSecurityFooter();
+        switchSecurityFooter(shouldUseSplitNotificationShade);
     }
 
     protected void setPageMargin(int pageMargin) {
@@ -674,6 +685,7 @@
                 mTileLayout.setMaxColumns(horizontal ? 2 : 4);
             }
             updateMargins(mediaHostView);
+            mHorizontalLinearLayout.setVisibility(horizontal ? View.VISIBLE : View.GONE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index ae0f510..f3d071e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -41,7 +41,6 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.brightness.BrightnessController;
 import com.android.systemui.settings.brightness.BrightnessSlider;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
 
@@ -77,6 +76,7 @@
                 refreshAllTiles();
             }
             updateBrightnessMirror();
+            mView.switchSecurityFooter(mShouldUseSplitNotificationShade);
         }
     };
 
@@ -101,10 +101,9 @@
             QSTileRevealController.Factory qsTileRevealControllerFactory,
             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
-            BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager,
-            FeatureFlags featureFlags) {
+            BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
-                metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
+                metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mQsSecurityFooter = qsSecurityFooter;
         mTunerService = tunerService;
         mQsCustomizerController = qsCustomizerController;
@@ -141,7 +140,7 @@
             refreshAllTiles();
         }
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        mView.setSecurityFooter(mQsSecurityFooter.getView());
+        mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
         switchTileLayout(true);
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 4739a3f..0da4814 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -35,7 +35,6 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.animation.DisappearParameters;
@@ -67,9 +66,8 @@
     private final UiEventLogger mUiEventLogger;
     private final QSLogger mQSLogger;
     private final DumpManager mDumpManager;
-    private final FeatureFlags mFeatureFlags;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
-    private boolean mShouldUseSplitNotificationShade;
+    protected boolean mShouldUseSplitNotificationShade;
 
     @Nullable
     private Consumer<Boolean> mMediaVisibilityChangedListener;
@@ -85,14 +83,17 @@
                 @Override
                 public void onConfigurationChange(Configuration newConfig) {
                     mShouldUseSplitNotificationShade =
-                            Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+                            Utils.shouldUseSplitNotificationShade(getResources());
                     if (newConfig.orientation != mLastOrientation) {
                         mLastOrientation = newConfig.orientation;
+                        onScreenRotated();
                         switchTileLayout(false);
                     }
                 }
             };
 
+    protected void onScreenRotated() { }
+
     private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
         if (mMediaVisibilityChangedListener != null) {
             mMediaVisibilityChangedListener.accept(visible);
@@ -115,8 +116,7 @@
             MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger,
             QSLogger qsLogger,
-            DumpManager dumpManager,
-            FeatureFlags featureFlags
+            DumpManager dumpManager
     ) {
         super(view);
         mHost = host;
@@ -127,9 +127,8 @@
         mUiEventLogger = uiEventLogger;
         mQSLogger = qsLogger;
         mDumpManager = dumpManager;
-        mFeatureFlags = featureFlags;
         mShouldUseSplitNotificationShade =
-                Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+                Utils.shouldUseSplitNotificationShade(getResources());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
new file mode 100644
index 0000000..7c81abc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 androidx.annotation.VisibleForTesting
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.settings.brightness.BrightnessSlider
+import javax.inject.Inject
+
+/**
+ * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for
+ * showing/hiding it when appropriate and (un)registering listeners
+ */
+class QuickQSBrightnessController @VisibleForTesting constructor(
+    private val brightnessControllerFactory: () -> BrightnessController
+) {
+
+    @Inject constructor(
+        brightnessControllerFactory: BrightnessController.Factory,
+        brightnessSliderFactory: BrightnessSlider.Factory,
+        quickQSPanel: QuickQSPanel
+    ) : this(brightnessControllerFactory = {
+            val slider = brightnessSliderFactory.create(quickQSPanel.context, quickQSPanel)
+            slider.init()
+            quickQSPanel.setBrightnessView(slider.rootView)
+            brightnessControllerFactory.create(slider)
+        })
+
+    private var isListening = false
+    private var brightnessController: BrightnessController? = null
+
+    fun init(shouldUseSplitNotificationShade: Boolean) {
+        refreshVisibility(shouldUseSplitNotificationShade)
+    }
+
+    /**
+     * Starts/Stops listening for brightness changing events.
+     * It's fine to call this function even if slider is not visible (which would be the case for
+     * all small screen devices), it will just do nothing in that case
+     */
+    fun setListening(listening: Boolean) {
+        if (listening) {
+            // controller can be null when slider was never shown
+            if (!isListening && brightnessController != null) {
+                brightnessController?.registerCallbacks()
+                isListening = true
+            }
+        } else {
+            brightnessController?.unregisterCallbacks()
+            isListening = false
+        }
+    }
+
+    fun checkRestrictionAndSetEnabled() {
+        brightnessController?.checkRestrictionAndSetEnabled()
+    }
+
+    fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) {
+        if (shouldUseSplitNotificationShade) {
+            showBrightnessSlider()
+        } else {
+            hideBrightnessSlider()
+        }
+    }
+
+    private fun hideBrightnessSlider() {
+        brightnessController?.hideSlider()
+    }
+
+    private fun showBrightnessSlider() {
+        if (brightnessController == null) {
+            brightnessController = brightnessControllerFactory()
+        }
+        brightnessController?.showSlider()
+        if (!isListening) {
+            brightnessController?.registerCallbacks()
+            isListening = true
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 4cd4048..f1c1e12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -33,23 +33,16 @@
  */
 public class QuickQSPanel extends QSPanel {
 
-    public static final String NUM_QUICK_TILES = "sysui_qqs_count";
     private static final String TAG = "QuickQSPanel";
-    // A default value so that we never return 0.
-    public static final int DEFAULT_MAX_TILES = 6;
+    // A fallback value for max tiles number when setting via Tuner (parseNumTiles)
+    public static final int TUNER_MAX_TILES_FALLBACK = 6;
 
     private boolean mDisabledByPolicy;
     private int mMaxTiles;
 
     public QuickQSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMaxTiles = Math.min(DEFAULT_MAX_TILES,
-                getResources().getInteger(R.integer.quick_qs_panel_max_columns));
-    }
-
-    @Override
-    public void setBrightnessView(View view) {
-        // Don't add brightness view
+        mMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
     }
 
     @Override
@@ -73,6 +66,11 @@
     }
 
     @Override
+    protected boolean mediaNeedsTopMargin() {
+        return true;
+    }
+
+    @Override
     protected void updatePadding() {
         // QS Panel is setting a top padding by default, which we don't need.
     }
@@ -101,7 +99,7 @@
     }
 
     public void setMaxTiles(int maxTiles) {
-        mMaxTiles = Math.min(maxTiles, DEFAULT_MAX_TILES);
+        mMaxTiles = maxTiles;
     }
 
     @Override
@@ -117,17 +115,18 @@
     }
 
     /**
-     * Parses the String setting into the number of tiles. Defaults to {@code mDefaultMaxTiles}
+     * Parses the String setting into the number of tiles. Defaults to
+     * {@link #TUNER_MAX_TILES_FALLBACK}
      *
      * @param numTilesValue value of the setting to parse
-     * @return parsed value of numTilesValue OR {@code mDefaultMaxTiles} on error
+     * @return parsed value of numTilesValue OR {@link #TUNER_MAX_TILES_FALLBACK} on error
      */
     public static int parseNumTiles(String numTilesValue) {
         try {
             return Integer.parseInt(numTilesValue);
         } catch (NumberFormatException e) {
             // Couldn't read an int from the new setting value. Use default.
-            return DEFAULT_MAX_TILES;
+            return TUNER_MAX_TILES_FALLBACK;
         }
     }
 
@@ -180,14 +179,13 @@
                     LayoutParams.WRAP_CONTENT);
             setLayoutParams(lp);
             setMaxColumns(4);
-            mLastRowPadding = true;
         }
 
         @Override
         public boolean updateResources() {
             mCellHeightResId = R.dimen.qs_quick_tile_size;
             boolean b = super.updateResources();
-            mMaxAllowedRows = 2;
+            mMaxAllowedRows = getResources().getInteger(R.integer.quick_qs_panel_max_rows);
             return b;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index fee56b9..74cd50c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -29,7 +29,6 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.statusbar.FeatureFlags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -43,22 +42,27 @@
 
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             newConfig -> {
-                int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+                int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
                 if (newMaxTiles != mView.getNumQuickTiles()) {
                     setMaxTiles(newMaxTiles);
                 }
             };
 
+    // brightness is visible only in split shade
+    private final QuickQSBrightnessController mBrightnessController;
+
     @Inject
     QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
             QSCustomizerController qsCustomizerController,
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager, FeatureFlags featureFlags
+            DumpManager dumpManager,
+            QuickQSBrightnessController quickQSBrightnessController
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
-                uiEventLogger, qsLogger, dumpManager, featureFlags);
+                uiEventLogger, qsLogger, dumpManager);
+        mBrightnessController = quickQSBrightnessController;
     }
 
     @Override
@@ -67,6 +71,7 @@
         mMediaHost.setExpansion(0.0f);
         mMediaHost.setShowsOnlyActiveMedia(true);
         mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+        mBrightnessController.init(mShouldUseSplitNotificationShade);
     }
 
     @Override
@@ -81,6 +86,12 @@
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
     }
 
+    @Override
+    void setListening(boolean listening) {
+        super.setListening(listening);
+        mBrightnessController.setListening(listening);
+    }
+
     public boolean isListening() {
         return mView.isListening();
     }
@@ -91,6 +102,17 @@
     }
 
     @Override
+    public void refreshAllTiles() {
+        mBrightnessController.checkRestrictionAndSetEnabled();
+        super.refreshAllTiles();
+    }
+
+    @Override
+    protected void onScreenRotated() {
+        mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
+    }
+
+    @Override
     public void setTiles() {
         List<QSTile> tiles = new ArrayList<>();
         for (QSTile tile : mHost.getTiles()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index cd9db61..0004759 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateView;
 
 import java.util.List;
 
@@ -62,11 +63,14 @@
     protected QuickQSPanel mHeaderQsPanel;
     private View mDatePrivacyView;
     private View mDateView;
+    // DateView next to clock. Visible on QQS
+    private VariableDateView mClockDateView;
     private View mSecurityHeaderView;
-    private View mClockIconsView;
+    private View mStatusIconsView;
     private View mContainer;
 
     private View mQSCarriers;
+    private ViewGroup mClockContainer;
     private Clock mClockView;
     private Space mDatePrivacySeparator;
     private View mClockIconsSeparator;
@@ -86,7 +90,6 @@
     private int mWaterfallTopInset;
     private int mCutOutPaddingLeft;
     private int mCutOutPaddingRight;
-    private float mViewAlpha = 1.0f;
     private float mKeyguardExpansionFraction;
     private int mTextColorPrimary = Color.TRANSPARENT;
     private int mTopViewMeasureHeight;
@@ -114,18 +117,20 @@
 
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
         mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
-        mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+        mStatusIconsView = findViewById(R.id.quick_qs_status_icons);
         mQSCarriers = findViewById(R.id.carrier_group);
         mContainer = findViewById(R.id.qs_container);
         mIconContainer = findViewById(R.id.statusIcons);
         mPrivacyChip = findViewById(R.id.privacy_chip);
         mDateView = findViewById(R.id.date);
+        mClockDateView = findViewById(R.id.date_clock);
         mSecurityHeaderView = findViewById(R.id.header_text_container);
         mClockIconsSeparator = findViewById(R.id.separator);
         mRightLayout = findViewById(R.id.rightLayout);
         mDateContainer = findViewById(R.id.date_container);
         mPrivacyContainer = findViewById(R.id.privacy_container);
 
+        mClockContainer = findViewById(R.id.clock_container);
         mClockView = findViewById(R.id.clock);
         mDatePrivacySeparator = findViewById(R.id.space);
         // Tint for the battery icons are handled in setupHost()
@@ -174,7 +179,7 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
             mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
-            updateAnimators();
+            post(this::updateAnimators);
         }
     }
 
@@ -205,6 +210,11 @@
 
     void updateResources() {
         Resources resources = mContext.getResources();
+        // status bar is already displayed out of QS in split shade
+        boolean shouldUseSplitShade =
+                resources.getBoolean(R.bool.config_use_split_notification_shade);
+        mStatusIconsView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
+        mDatePrivacyView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
 
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
@@ -216,13 +226,13 @@
                 Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
         mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
 
-        mClockIconsView.getLayoutParams().height =
-                Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
-        mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
+        mStatusIconsView.getLayoutParams().height =
+                Math.max(qsOffsetHeight, mStatusIconsView.getMinimumHeight());
+        mStatusIconsView.setLayoutParams(mStatusIconsView.getLayoutParams());
 
         ViewGroup.LayoutParams lp = getLayoutParams();
         if (mQsDisabled) {
-            lp.height = mClockIconsView.getLayoutParams().height;
+            lp.height = mStatusIconsView.getLayoutParams().height;
         } else {
             lp.height = WRAP_CONTENT;
         }
@@ -266,7 +276,8 @@
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
                 // These views appear on expanding down
-                .addFloat(mClockView, "alpha", 0, 1)
+                .addFloat(mDateView, "alpha", 0, 0, 1)
+                .addFloat(mClockDateView, "alpha", 1, 0, 0)
                 .addFloat(mQSCarriers, "alpha", 0, 1)
                 .setListener(new TouchAnimator.ListenerAdapter() {
                     @Override
@@ -275,10 +286,14 @@
                         if (!mIsSingleCarrier) {
                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
                         }
+                        // Make it gone so there's enough room for carrier names
+                        mClockDateView.setVisibility(View.GONE);
                     }
 
                     @Override
                     public void onAnimationStarted() {
+                        mClockDateView.setVisibility(View.VISIBLE);
+                        mClockDateView.setFreezeSwitching(true);
                         setSeparatorVisibility(false);
                         if (!mIsSingleCarrier) {
                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
@@ -288,6 +303,7 @@
                     @Override
                     public void onAnimationAtStart() {
                         super.onAnimationAtStart();
+                        mClockDateView.setFreezeSwitching(false);
                         setSeparatorVisibility(mShowClockIconsSeparator);
                         // In QQS we never ignore RSSI.
                         mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
@@ -355,7 +371,7 @@
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
         mHeaderQsPanel.setDisabledByPolicy(disabled);
-        mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
         updateResources();
     }
 
@@ -369,7 +385,7 @@
                 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
                         cutout, cornerCutoutPadding, -1);
         mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
-        mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
+        mStatusIconsView.setPadding(padding.first, 0, padding.second, 0);
         LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
                 (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
         LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
@@ -418,10 +434,11 @@
         mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
         mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
 
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+        LinearLayout.LayoutParams lp =
+                (LinearLayout.LayoutParams) mClockContainer.getLayoutParams();
         lp.width = visible ? 0 : WRAP_CONTENT;
         lp.weight = visible ? 1f : 0f;
-        mClockView.setLayoutParams(lp);
+        mClockContainer.setLayoutParams(lp);
 
         lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
         lp.width = visible ? 0 : WRAP_CONTENT;
@@ -431,7 +448,7 @@
 
     private void updateHeadersPadding() {
         setContentMargins(mDatePrivacyView, 0, 0);
-        setContentMargins(mClockIconsView, 0, 0);
+        setContentMargins(mStatusIconsView, 0, 0);
         int paddingLeft = 0;
         int paddingRight = 0;
 
@@ -457,7 +474,7 @@
                 mWaterfallTopInset,
                 paddingRight,
                 0);
-        mClockIconsView.setPadding(paddingLeft,
+        mStatusIconsView.setPadding(paddingLeft,
                 mWaterfallTopInset,
                 paddingRight,
                 0);
@@ -484,7 +501,7 @@
      * @param scrollY the scroll of the QSPanel container
      */
     public void setExpandedScrollAmount(int scrollY) {
-        mClockIconsView.setScrollY(scrollY);
+        mStatusIconsView.setScrollY(scrollY);
         mDatePrivacyView.setScrollY(scrollY);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index b8b7f42..1343839 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateViewController;
 import com.android.systemui.util.ViewController;
 
 import java.util.List;
@@ -58,7 +59,7 @@
     private final ActivityStarter mActivityStarter;
     private final UiEventLogger mUiEventLogger;
     private final QSCarrierGroupController mQSCarrierGroupController;
-    private final QuickQSPanelController mHeaderQsPanelController;
+    private final QuickQSPanelController mQuickQSPanelController;
     private final OngoingPrivacyChip mPrivacyChip;
     private final Clock mClockView;
     private final StatusBarIconController mStatusBarIconController;
@@ -71,6 +72,9 @@
     private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
     private final FeatureFlags mFeatureFlags;
 
+    private final VariableDateViewController mVariableDateViewControllerDateView;
+    private final VariableDateViewController mVariableDateViewControllerClockDateView;
+
     private boolean mListening;
     private boolean mMicCameraIndicatorsEnabled;
     private boolean mLocationIndicatorsEnabled;
@@ -134,14 +138,15 @@
             SysuiColorExtractor colorExtractor,
             PrivacyDialogController privacyDialogController,
             QSExpansionPathInterpolator qsExpansionPathInterpolator,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            VariableDateViewController.Factory variableDateViewControllerFactory) {
         super(view);
         mPrivacyItemController = privacyItemController;
         mActivityStarter = activityStarter;
         mUiEventLogger = uiEventLogger;
         mStatusBarIconController = statusBarIconController;
         mDemoModeController = demoModeController;
-        mHeaderQsPanelController = quickQSPanelController;
+        mQuickQSPanelController = quickQSPanelController;
         mPrivacyLogger = privacyLogger;
         mPrivacyDialogController = privacyDialogController;
         mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
@@ -154,6 +159,12 @@
         mPrivacyChip = mView.findViewById(R.id.privacy_chip);
         mClockView = mView.findViewById(R.id.clock);
         mIconContainer = mView.findViewById(R.id.statusIcons);
+        mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
+                mView.requireViewById(R.id.date)
+        );
+        mVariableDateViewControllerClockDateView = variableDateViewControllerFactory.create(
+                mView.requireViewById(R.id.date_clock)
+        );
 
         mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags);
         mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
@@ -203,6 +214,9 @@
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots);
 
         mDemoModeController.addCallback(mDemoModeReceiver);
+
+        mVariableDateViewControllerDateView.init();
+        mVariableDateViewControllerClockDateView.init();
     }
 
     @Override
@@ -223,12 +237,12 @@
         }
         mListening = listening;
 
-        mHeaderQsPanelController.setListening(listening);
-        if (mHeaderQsPanelController.isListening()) {
-            mHeaderQsPanelController.refreshAllTiles();
+        mQuickQSPanelController.setListening(listening);
+        if (mQuickQSPanelController.isListening()) {
+            mQuickQSPanelController.refreshAllTiles();
         }
 
-        if (mHeaderQsPanelController.switchTileLayout(false)) {
+        if (mQuickQSPanelController.switchTileLayout(false)) {
             mView.updateResources();
         }
 
@@ -284,7 +298,7 @@
     }
 
     public void setContentMargins(int marginStart, int marginEnd) {
-        mHeaderQsPanelController.setContentMargins(marginStart, marginEnd);
+        mQuickQSPanelController.setContentMargins(marginStart, marginEnd);
     }
 
     private static class ClockDemoModeReceiver implements DemoMode {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 2b96a34..1a890a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -31,7 +31,6 @@
     protected int mCellMarginVertical;
     protected int mSidePadding;
     protected int mRows = 1;
-    protected boolean mLastRowPadding = false;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     protected boolean mListening;
@@ -168,9 +167,7 @@
         }
 
         int height = (mCellHeight + mCellMarginVertical) * mRows;
-        if (!mLastRowPadding) {
-            height -= mCellMarginVertical;
-        }
+        height -= mCellMarginVertical;
 
         if (height < 0) height = 0;
 
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 7518b20..d33982c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -84,8 +84,8 @@
 
     void updateResources() {
         LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
-        lp.height = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
+        lp.height = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
         mTransparentView.setLayoutParams(lp);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 4d63349..36a1910 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -33,7 +33,6 @@
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.QuickStatusBarHeader;
-import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
 
@@ -146,9 +145,4 @@
     static boolean providesQSUsingMediaPlayer(Context context) {
         return useQsMediaPlayer(context);
     }
-
-    /** */
-    @Binds
-    QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
-            QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b13015..04f089d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -150,11 +150,7 @@
         }
 
         List<CastDevice> activeDevices = getActiveDevices();
-        // We want to pop up the media route selection dialog if we either have no active devices
-        // (neither routes nor projection), or if we have an active route. In other cases, we assume
-        // that a projection is active. This is messy, but this tile never correctly handled the
-        // case where multiple devices were active :-/.
-        if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+        if (willPopDetail()) {
             mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                 showDetail(true);
             });
@@ -163,6 +159,15 @@
         }
     }
 
+    // We want to pop up the media route selection dialog if we either have no active devices
+    // (neither routes nor projection), or if we have an active route. In other cases, we assume
+    // that a projection is active. This is messy, but this tile never correctly handled the
+    // case where multiple devices were active :-/.
+    private boolean willPopDetail() {
+        List<CastDevice> activeDevices = getActiveDevices();
+        return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+    }
+
     private List<CastDevice> getActiveDevices() {
         ArrayList<CastDevice> activeDevices = new ArrayList<>();
         for (CastDevice device : mController.getCastDevices()) {
@@ -234,10 +239,12 @@
             state.contentDescription = state.contentDescription + ","
                     + mContext.getString(R.string.accessibility_quick_settings_open_details);
             state.expandedAccessibilityClassName = Button.class.getName();
+            state.forceExpandIcon = willPopDetail();
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
             String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
             state.secondaryLabel = noWifi;
+            state.forceExpandIcon = false;
         }
         state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
         mDetailAdapter.updateItems(devices);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 7cb1421..92ae88d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,7 +51,9 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
@@ -66,15 +68,16 @@
 /** Quick settings tile: Internet **/
 public class InternetTile extends QSTileImpl<SignalState> {
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
-    private static final Intent INTERNET_PANEL =
-            new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
 
     protected final NetworkController mController;
+    private final AccessPointController mAccessPointController;
     private final DataUsageController mDataController;
     // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
     private int mLastTileState = -1;
 
     protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+    private final InternetDialogFactory mInternetDialogFactory;
+    final Handler mHandler;
 
     @Inject
     public InternetTile(
@@ -86,11 +89,16 @@
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
-            NetworkController networkController
+            NetworkController networkController,
+            AccessPointController accessPointController,
+            InternetDialogFactory internetDialogFactory
     ) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
+        mInternetDialogFactory = internetDialogFactory;
+        mHandler = mainHandler;
         mController = networkController;
+        mAccessPointController = accessPointController;
         mDataController = mController.getMobileDataController();
         mController.observe(getLifecycle(), mSignalCallback);
     }
@@ -114,7 +122,8 @@
 
     @Override
     protected void handleClick(@Nullable View view) {
-        mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
+        boolean canConfigMobileData = mAccessPointController.canConfigMobileData();
+        mHandler.post(() -> mInternetDialogFactory.create(true, canConfigMobileData));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
new file mode 100644
index 0000000..44c1b7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tiles.dialog;
+
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+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 androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * Adapter for showing Wi-Fi networks.
+ */
+public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
+
+    private static final String TAG = "InternetAdapter";
+    private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
+    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+    private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final InternetDialogController mInternetDialogController;
+
+    protected View mHolderView;
+    protected Context mContext;
+
+    public InternetAdapter(InternetDialogController controller) {
+        mInternetDialogController = controller;
+    }
+
+    @Override
+    public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+            int viewType) {
+        mContext = viewGroup.getContext();
+        mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
+                viewGroup, false);
+        return new InternetViewHolder(mHolderView, mInternetDialogController);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
+        List<WifiEntry> wifiList = getWifiEntryList();
+        if (wifiList != null && wifiList.size() != 0) {
+            int count = getItemCount();
+            if (wifiList.size() > count) {
+                wifiList = getWifiEntryList().subList(0, count - 1);
+            }
+
+            if (position < wifiList.size()) {
+                viewHolder.onBind(wifiList.get(position));
+            }
+        } else if (DEBUG) {
+            Log.d(TAG, "onBindViewHolder, Wi-Fi entry list = null");
+        }
+    }
+
+    private List<WifiEntry> getWifiEntryList() {
+        if (mInternetDialogController.getWifiEntryList() == null) {
+            return null;
+        }
+
+        return mInternetDialogController.getWifiEntryList().stream()
+                .filter(wifiAp -> wifiAp.getConnectedState()
+                        != WifiEntry.CONNECTED_STATE_CONNECTED)
+                .limit(getItemCount())
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * The total number of networks (mobile network and entries of Wi-Fi) should be four in
+     * {@link InternetDialog}.
+     *
+     * Airplane mode is ON (mobile network is gone):
+     *   Return four Wi-Fi's entries if no connected Wi-Fi.
+     *   Return three Wi-Fi's entries if one connected Wi-Fi.
+     * Airplane mode is OFF (mobile network is visible):
+     *   Return three Wi-Fi's entries if no connected Wi-Fi.
+     *   Return two Wi-Fi's entries if one connected Wi-Fi.
+     *
+     * @return The total number of networks.
+     */
+    @Override
+    public int getItemCount() {
+        boolean hasConnectedWifi = mInternetDialogController.getConnectedWifiEntry() != null;
+        if (mInternetDialogController.isAirplaneModeEnabled()) {
+            return hasConnectedWifi ? 3 : 4;
+        } else {
+            return hasConnectedWifi ? 2 : 3;
+        }
+    }
+
+    /**
+     * ViewHolder for binding Wi-Fi view.
+     */
+    static class InternetViewHolder extends RecyclerView.ViewHolder {
+
+        final LinearLayout mContainerLayout;
+        final LinearLayout mWifiListLayout;
+        final LinearLayout mWifiNetworkLayout;
+        final ImageView mWifiIcon;
+        final TextView mWifiTitleText;
+        final TextView mWifiSummaryText;
+        final ImageView mWifiLockedIcon;
+        final Context mContext;
+        final InternetDialogController mInternetDialogController;
+
+        InternetViewHolder(View view, InternetDialogController internetDialogController) {
+            super(view);
+            mContext = view.getContext();
+            mInternetDialogController = internetDialogController;
+            mContainerLayout = view.requireViewById(R.id.internet_container);
+            mWifiListLayout = view.requireViewById(R.id.wifi_list);
+            mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
+            mWifiIcon = view.requireViewById(R.id.wifi_icon);
+            mWifiTitleText = view.requireViewById(R.id.wifi_title);
+            mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
+            mWifiLockedIcon = view.requireViewById(R.id.wifi_locked_icon);
+        }
+
+        void onBind(WifiEntry wifiEntry) {
+            int security = wifiEntry.getSecurity();
+            try {
+                mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+                if (isOpenNetwork(security)) {
+                    mWifiLockedIcon.setVisibility(View.GONE);
+                } else {
+                    mWifiLockedIcon.setVisibility(View.VISIBLE);
+                    mWifiLockedIcon.setImageDrawable(
+                            mContext.getDrawable(R.drawable.ic_friction_lock_closed));
+                }
+            } catch (Throwable throwable) {
+                throwable.printStackTrace();
+            }
+
+            setWifiNetworkLayout(wifiEntry.getTitle(),
+                    Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
+
+            mWifiListLayout.setOnClickListener(v -> {
+                if (wifiEntry.shouldEditBeforeConnect()) {
+                    final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+                    intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
+                    intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
+                    mContext.startActivity(intent);
+                }
+                mInternetDialogController.connect(wifiEntry);
+            });
+        }
+
+        /** Return true if this is an open network AccessPoint. */
+        boolean isOpenNetwork(int security) {
+            return security == SECURITY_NONE
+                    || security == SECURITY_OWE;
+        }
+
+        void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
+            mWifiNetworkLayout.setVisibility(View.VISIBLE);
+            mWifiTitleText.setText(title);
+            if (TextUtils.isEmpty(summary)) {
+                mWifiTitleText.setGravity(Gravity.CENTER);
+                mWifiSummaryText.setVisibility(View.GONE);
+                return;
+            } else {
+                mWifiTitleText.setGravity(Gravity.BOTTOM);
+                mWifiSummaryText.setGravity(Gravity.TOP);
+                mWifiSummaryText.setVisibility(View.VISIBLE);
+            }
+            mWifiSummaryText.setText(summary);
+        }
+
+        Drawable getWifiDrawable(WifiEntry wifiEntry) throws Throwable {
+            Drawable drawable = mContext.getDrawable(
+                    com.android.internal.R.drawable.ic_wifi_signal_0);
+
+            AtomicReference<Drawable> shared = new AtomicReference<>();
+            final @ColorInt int tint = Utils.getColorAttrDefaultColor(mContext,
+                    android.R.attr.colorControlNormal);
+            Drawable signalDrawable = mContext.getDrawable(
+                    Utils.getWifiIconResource(wifiEntry.getLevel()));
+            signalDrawable.setTint(tint);
+            shared.set(signalDrawable);
+            drawable = shared.get();
+            return drawable;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
new file mode 100644
index 0000000..ce1c2fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tiles.dialog;
+
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+
+import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.Space;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+
+/**
+ * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
+ */
+@SysUISingleton
+public class InternetDialog extends SystemUIDialog implements
+        InternetDialogController.InternetDialogCallback, Window.Callback {
+    private static final String TAG = "InternetDialog";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private final Handler mHandler;
+    private final LinearLayoutManager mLayoutManager;
+    private final Runnable mHideProgressBarRunnable = () -> {
+        setProgressBarVisible(false);
+    };
+
+    @VisibleForTesting
+    protected InternetAdapter mAdapter;
+    @VisibleForTesting
+    protected WifiManager mWifiManager;
+    @VisibleForTesting
+    protected View mDialogView;
+
+    private InternetDialogFactory mInternetDialogFactory;
+    private SubscriptionManager mSubscriptionManager;
+    private TelephonyManager mTelephonyManager;
+    private AlertDialog mAlertDialog;
+    private UiEventLogger mUiEventLogger;
+    private Context mContext;
+    private InternetDialogController mInternetDialogController;
+    private TextView mInternetDialogTitle;
+    private TextView mInternetDialogSubTitle;
+    private ProgressBar mProgressBar;
+    private LinearLayout mInternetListLayout;
+    private LinearLayout mConnectedWifListLayout;
+    private LinearLayout mConnectedWifList;
+    private LinearLayout mMobileNetworkLayout;
+    private LinearLayout mMobileNetworkList;
+    private LinearLayout mTurnWifiOnLayout;
+    private LinearLayout mSeeAllLayout;
+    private Space mSpace;
+    private RecyclerView mWifiRecyclerView;
+    private ImageView mConnectedWifiIcon;
+    private ImageView mWifiSettingsIcon;
+    private TextView mConnectedWifiTitleText;
+    private TextView mConnectedWifiSummaryText;
+    private ImageView mSignalIcon;
+    private TextView mMobileTitleText;
+    private TextView mMobileSummaryText;
+    private Switch mMobileDataToggle;
+    private Switch mWiFiToggle;
+    private Button mDoneButton;
+    private Drawable mBackgroundOn;
+    private WifiEntry mConnectedWifiEntry;
+    private int mListMaxHeight;
+    private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private boolean mIsProgressBarVisible;
+    private boolean mCanConfigMobileData;
+
+    private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
+        // Set max height for list
+        if (mInternetListLayout.getHeight() > mListMaxHeight) {
+            ViewGroup.LayoutParams params = mInternetListLayout.getLayoutParams();
+            params.height = mListMaxHeight;
+            mInternetListLayout.setLayoutParams(params);
+        }
+    };
+
+    public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+            InternetDialogController internetDialogController, boolean canConfigMobileData,
+            boolean aboveStatusBar, UiEventLogger uiEventLogger, @Main Handler handler) {
+        super(context, R.style.Theme_SystemUI_Dialog_Internet);
+        if (DEBUG) {
+            Log.d(TAG, "Init InternetDialog");
+        }
+        mContext = context;
+        mHandler = handler;
+        mInternetDialogFactory = internetDialogFactory;
+        mInternetDialogController = internetDialogController;
+        mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
+        mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
+        mTelephonyManager = mInternetDialogController.getTelephonyManager();
+        mWifiManager = mInternetDialogController.getWifiManager();
+        mCanConfigMobileData = canConfigMobileData;
+
+        mLayoutManager = new LinearLayoutManager(mContext) {
+            @Override
+            public boolean canScrollVertically() {
+                return false;
+            }
+        };
+        mListMaxHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.internet_dialog_list_max_height);
+        mUiEventLogger = uiEventLogger;
+        mAdapter = new InternetAdapter(mInternetDialogController);
+        if (!aboveStatusBar) {
+            getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (DEBUG) {
+            Log.d(TAG, "onCreate");
+        }
+        mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
+        mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
+                null);
+        final Window window = getWindow();
+        final WindowManager.LayoutParams layoutParams = window.getAttributes();
+        layoutParams.gravity = Gravity.BOTTOM;
+        // Move down the dialog to overlay the navigation bar.
+        layoutParams.setFitInsetsTypes(
+                layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
+        layoutParams.setFitInsetsSides(WindowInsets.Side.all());
+        layoutParams.setFitInsetsIgnoringVisibility(true);
+        window.setAttributes(layoutParams);
+        window.setContentView(mDialogView);
+        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        window.setWindowAnimations(R.style.Animation_InternetDialog);
+        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        window.addFlags(FLAG_LAYOUT_NO_LIMITS);
+
+        mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
+        mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+        mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
+        mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+        mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+        mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
+        mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
+        mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
+        mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
+        mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
+        mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
+        mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
+        mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
+        mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
+        mSpace = mDialogView.requireViewById(R.id.space);
+        mDoneButton = mDialogView.requireViewById(R.id.done);
+        mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
+        mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
+        mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+        mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+        mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+        mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+        mInternetListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+                mInternetListLayoutListener);
+        mInternetDialogTitle.setText(getDialogTitleText());
+        mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+
+        setOnClickListener();
+        mTurnWifiOnLayout.setBackground(null);
+        mWifiRecyclerView.setLayoutManager(mLayoutManager);
+        mWifiRecyclerView.setAdapter(mAdapter);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (DEBUG) {
+            Log.d(TAG, "onStart");
+        }
+        mInternetDialogController.onStart(this);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (DEBUG) {
+            Log.d(TAG, "onStop");
+        }
+        mHandler.removeCallbacks(mHideProgressBarRunnable);
+        mMobileNetworkLayout.setOnClickListener(null);
+        mMobileDataToggle.setOnCheckedChangeListener(null);
+        mConnectedWifListLayout.setOnClickListener(null);
+        mSeeAllLayout.setOnClickListener(null);
+        mWiFiToggle.setOnCheckedChangeListener(null);
+        mDoneButton.setOnClickListener(null);
+        mInternetDialogController.onStop();
+        mInternetDialogFactory.destroyDialog();
+    }
+
+    @Override
+    public void dismissDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "dismissDialog");
+        }
+        mInternetDialogFactory.destroyDialog();
+        dismiss();
+    }
+
+    void updateDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "updateDialog");
+        }
+        if (mInternetDialogController.isAirplaneModeEnabled()) {
+            mInternetDialogSubTitle.setVisibility(View.GONE);
+        } else {
+            mInternetDialogSubTitle.setText(getSubtitleText());
+        }
+        showProgressBar();
+        setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+        setConnectedWifiLayout();
+        boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+        mWiFiToggle.setChecked(isWifiEnabled);
+        int visible = isWifiEnabled ? View.VISIBLE : View.GONE;
+        mWifiRecyclerView.setVisibility(visible);
+        mAdapter.notifyDataSetChanged();
+        mSeeAllLayout.setVisibility(visible);
+        mSpace.setVisibility(isWifiEnabled ? View.GONE : View.VISIBLE);
+    }
+
+    private void setOnClickListener() {
+        mMobileNetworkLayout.setOnClickListener(v -> {
+            if (mInternetDialogController.isMobileDataEnabled()) {
+                if (!mInternetDialogController.activeNetworkIsCellular()) {
+                    mInternetDialogController.connectCarrierNetwork();
+                }
+            }
+        });
+        mMobileDataToggle.setOnCheckedChangeListener(
+                (buttonView, isChecked) -> {
+                    if (!isChecked && shouldShowMobileDialog()) {
+                        showTurnOffMobileDialog();
+                    } else if (!shouldShowMobileDialog()) {
+                        mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
+                                isChecked, false);
+                    }
+                });
+        mConnectedWifListLayout.setOnClickListener(v -> onClickConnectedWifi());
+        mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+        mWiFiToggle.setOnCheckedChangeListener(
+                (buttonView, isChecked) -> {
+                    buttonView.setChecked(isChecked);
+                    mWifiManager.setWifiEnabled(isChecked);
+                    mSpace.setVisibility(isChecked ? View.GONE : View.VISIBLE);
+                });
+        mDoneButton.setOnClickListener(v -> dismiss());
+    }
+
+    private void setMobileDataLayout(boolean isCellularNetwork) {
+        if (mInternetDialogController.isAirplaneModeEnabled()
+                || !mInternetDialogController.hasCarrier()) {
+            mMobileNetworkLayout.setVisibility(View.GONE);
+        } else {
+            mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+            mMobileNetworkLayout.setVisibility(View.VISIBLE);
+            mMobileTitleText.setText(getMobileNetworkTitle());
+            if (!TextUtils.isEmpty(getMobileNetworkSummary())) {
+                mMobileSummaryText.setText(
+                        Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+                mMobileSummaryText.setVisibility(View.VISIBLE);
+            } else {
+                mMobileSummaryText.setVisibility(View.GONE);
+            }
+            mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
+            int titleColor = isCellularNetwork ? mContext.getColor(
+                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+                    mContext, android.R.attr.textColorPrimary);
+            int summaryColor = isCellularNetwork ? mContext.getColor(
+                    R.color.connected_network_tertiary_color) : Utils.getColorAttrDefaultColor(
+                    mContext, android.R.attr.textColorTertiary);
+            mMobileTitleText.setTextColor(titleColor);
+            mMobileSummaryText.setTextColor(summaryColor);
+            mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+
+            mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private void setConnectedWifiLayout() {
+        if (!mWifiManager.isWifiEnabled()
+                || mInternetDialogController.getConnectedWifiEntry() == null) {
+            mConnectedWifListLayout.setBackground(null);
+            mConnectedWifListLayout.setVisibility(View.GONE);
+            return;
+        }
+        mConnectedWifListLayout.setVisibility(View.VISIBLE);
+        mConnectedWifiTitleText.setText(getConnectedWifiTitle());
+        mConnectedWifiSummaryText.setText(getConnectedWifiSummary());
+        mConnectedWifiIcon.setImageDrawable(getConnectedWifiDrawable());
+        mConnectedWifiTitleText.setTextColor(
+                mContext.getColor(R.color.connected_network_primary_color));
+        mConnectedWifiSummaryText.setTextColor(
+                mContext.getColor(R.color.connected_network_tertiary_color));
+        mWifiSettingsIcon.setColorFilter(
+                mContext.getColor(R.color.connected_network_primary_color));
+        mConnectedWifListLayout.setBackground(mBackgroundOn);
+    }
+
+    void onClickConnectedWifi() {
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+    }
+
+    void onClickSeeMoreButton() {
+        mInternetDialogController.launchNetworkSetting();
+    }
+
+    CharSequence getDialogTitleText() {
+        return mInternetDialogController.getDialogTitleText();
+    }
+
+    CharSequence getSubtitleText() {
+        return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
+    }
+
+    private Drawable getConnectedWifiDrawable() {
+        try {
+            return mInternetDialogController.getWifiConnectedDrawable(mConnectedWifiEntry);
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private Drawable getSignalStrengthDrawable() {
+        return mInternetDialogController.getSignalStrengthDrawable();
+    }
+
+    CharSequence getMobileNetworkTitle() {
+        return mInternetDialogController.getMobileNetworkTitle();
+    }
+
+    String getMobileNetworkSummary() {
+        return mInternetDialogController.getMobileNetworkSummary();
+    }
+
+    String getConnectedWifiTitle() {
+        return mInternetDialogController.getConnectedWifiTitle();
+    }
+
+    String getConnectedWifiSummary() {
+        return mInternetDialogController.getConnectedWifiSummary();
+    }
+
+    private void showProgressBar() {
+        if (mWifiManager == null || !mWifiManager.isWifiEnabled()) {
+            setProgressBarVisible(false);
+            return;
+        }
+        setProgressBarVisible(true);
+        List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
+        if (wifiScanResults != null && wifiScanResults.size() > 0) {
+            mContext.getMainThreadHandler().postDelayed(mHideProgressBarRunnable,
+                    2000 /* delay millis */);
+        }
+    }
+
+    private void setProgressBarVisible(boolean visible) {
+        if (mWifiManager.isWifiEnabled() && mAdapter.mHolderView != null
+                && mAdapter.mHolderView.isAttachedToWindow()) {
+            mIsProgressBarVisible = true;
+        }
+        mIsProgressBarVisible = visible;
+        mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.INVISIBLE);
+        mInternetDialogSubTitle.setText(getSubtitleText());
+    }
+
+    private boolean shouldShowMobileDialog() {
+        boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+                false);
+        if (mInternetDialogController.isMobileDataEnabled() && !flag) {
+            return true;
+        }
+        return false;
+    }
+
+    private void showTurnOffMobileDialog() {
+        CharSequence carrierName =
+                mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
+        boolean isInService = mInternetDialogController.isVoiceStateInService();
+        if (TextUtils.isEmpty(carrierName) || !isInService) {
+            carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+        }
+        mAlertDialog = new Builder(mContext)
+                .setTitle(R.string.mobile_data_disable_title)
+                .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+                .setNegativeButton(android.R.string.cancel, (d, w) -> {
+                    mMobileDataToggle.setChecked(true);
+                })
+                .setPositiveButton(
+                        com.android.internal.R.string.alert_windows_notification_turn_off_action,
+                        (d, w) -> {
+                            mInternetDialogController.setMobileDataEnabled(mContext,
+                                    mDefaultDataSubId, false, false);
+                            mMobileDataToggle.setChecked(false);
+                            Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+                        })
+                .create();
+        mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true));
+        mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+        SystemUIDialog.registerDismissListener(mAlertDialog);
+        SystemUIDialog.setWindowOnTop(mAlertDialog);
+        mAlertDialog.show();
+    }
+
+    @Override
+    public void onRefreshCarrierInfo() {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSimStateChanged() {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSubscriptionsChanged(int defaultDataSubId) {
+        mDefaultDataSubId = defaultDataSubId;
+        mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onServiceStateChanged(ServiceState serviceState) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onDataConnectionStateChanged(int state, int networkType) {
+        mAdapter.notifyDataSetChanged();
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry) {
+        mConnectedWifiEntry = connectedEntry;
+        mAdapter.notifyDataSetChanged();
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (mAlertDialog != null && !mAlertDialog.isShowing()) {
+            if (!hasFocus && isShowing()) {
+                dismiss();
+            }
+        }
+    }
+
+    @Override
+    public void onWifiStateReceived(Context context, Intent intent) {
+        if (intent == null) {
+            return;
+        }
+
+        String action = intent.getAction();
+        if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+            mInternetDialogController.scanWifiAccessPoints();
+            showProgressBar();
+            return;
+        }
+
+        if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            mHandler.post(() -> updateDialog());
+        }
+    }
+
+    public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The Internet dialog became visible on the screen.")
+        INTERNET_DIALOG_SHOW(843);
+
+        private final int mId;
+
+        InternetDialogEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
new file mode 100644
index 0000000..edab33a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tiles.dialog;
+
+import static com.android.settingslib.mobile.MobileMappings.getIconKey;
+import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+
+import android.annotation.ColorInt;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.Utils;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.net.SignalStrengthUtil;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.wifitrackerlib.MergedCarrierEntry;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+public class InternetDialogController implements WifiEntry.DisconnectCallback,
+        NetworkController.AccessPointController.AccessPointCallback {
+
+    private static final String TAG = "InternetDialogController";
+    private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+            "android.settings.NETWORK_PROVIDER_SETTINGS";
+    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+    public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+    public static final int NO_CELL_DATA_TYPE_ICON = 0;
+    private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
+    private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
+            R.string.tap_a_network_to_connect;
+    private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
+            R.string.wifi_empty_list_wifi_on;
+    private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
+            R.string.non_carrier_network_unavailable;
+    private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
+            R.string.all_network_unavailable;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private WifiManager mWifiManager;
+    private Context mContext;
+    private SubscriptionManager mSubscriptionManager;
+    private TelephonyManager mTelephonyManager;
+    private ConnectivityManager mConnectivityManager;
+    private TelephonyDisplayInfo mTelephonyDisplayInfo =
+            new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+    private Handler mHandler;
+    private MobileMappings.Config mConfig = null;
+    private Executor mExecutor;
+    private AccessPointController mAccessPointController;
+    private IntentFilter mConnectionStateFilter;
+    private InternetDialogCallback mCallback;
+    private List<WifiEntry> mWifiEntry;
+    private UiEventLogger mUiEventLogger;
+    private BroadcastDispatcher mBroadcastDispatcher;
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private GlobalSettings mGlobalSettings;
+    private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    @VisibleForTesting
+    protected ActivityStarter mActivityStarter;
+    @VisibleForTesting
+    protected WifiEntry mConnectedEntry;
+    @VisibleForTesting
+    protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
+    @VisibleForTesting
+    protected InternetTelephonyCallback mInternetTelephonyCallback;
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onRefreshCarrierInfo() {
+                    mCallback.onRefreshCarrierInfo();
+                }
+
+                @Override
+                public void onSimStateChanged(int subId, int slotId, int simState) {
+                    mCallback.onSimStateChanged();
+                }
+            };
+
+    protected List<SubscriptionInfo> getSubscriptionInfo() {
+        return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+    }
+
+    @Inject
+    public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
+            ActivityStarter starter, AccessPointController accessPointController,
+            SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+            @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+            @Main Handler handler, @Main Executor mainExecutor,
+            BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
+            GlobalSettings globalSettings) {
+        if (DEBUG) {
+            Log.d(TAG, "Init InternetDialogController");
+        }
+        mHandler = handler;
+        mExecutor = mainExecutor;
+        mContext = context;
+        mGlobalSettings = globalSettings;
+        mWifiManager = wifiManager;
+        mTelephonyManager = telephonyManager;
+        mConnectivityManager = connectivityManager;
+        mSubscriptionManager = subscriptionManager;
+        mBroadcastDispatcher = broadcastDispatcher;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mConnectionStateFilter = new IntentFilter();
+        mConnectionStateFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        mConnectionStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        mUiEventLogger = uiEventLogger;
+        mActivityStarter = starter;
+        mAccessPointController = accessPointController;
+        mConfig = MobileMappings.Config.readConfig(mContext);
+    }
+
+    void onStart(@NonNull InternetDialogCallback callback) {
+        if (DEBUG) {
+            Log.d(TAG, "onStart");
+        }
+        mCallback = callback;
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+        mAccessPointController.addAccessPointCallback(this);
+        mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
+                mExecutor);
+        // Listen the subscription changes
+        mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+                mOnSubscriptionsChangedListener);
+        mDefaultDataSubId = getDefaultDataSubscriptionId();
+        if (DEBUG) {
+            Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
+        }
+        mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+        mInternetTelephonyCallback = new InternetTelephonyCallback();
+        mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
+        // Listen the connectivity changes
+        mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build(), new DataConnectivityListener(), mHandler);
+        scanWifiAccessPoints();
+    }
+
+    void onStop() {
+        if (DEBUG) {
+            Log.d(TAG, "onStop");
+        }
+        mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
+        mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+        mSubscriptionManager.removeOnSubscriptionsChangedListener(
+                mOnSubscriptionsChangedListener);
+        mAccessPointController.removeAccessPointCallback(this);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+    }
+
+    @VisibleForTesting
+    boolean isAirplaneModeEnabled() {
+        return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+    }
+
+    @VisibleForTesting
+    protected int getDefaultDataSubscriptionId() {
+        return mSubscriptionManager.getDefaultDataSubscriptionId();
+    }
+
+    @VisibleForTesting
+    protected Intent getSettingsIntent() {
+        return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    protected Intent getWifiDetailsSettingsIntent() {
+        String key = mConnectedEntry == null ? null : mConnectedEntry.getKey();
+        if (TextUtils.isEmpty(key)) {
+            if (DEBUG) {
+                Log.d(TAG, "connected entry's key is empty");
+            }
+            return null;
+        }
+        return WifiUtils.getWifiDetailsSettingsIntent(key);
+    }
+
+    CharSequence getDialogTitleText() {
+        if (isAirplaneModeEnabled()) {
+            return mContext.getText(R.string.airplane_mode);
+        }
+        return mContext.getText(R.string.quick_settings_internet_label);
+    }
+
+    CharSequence getSubtitleText(boolean isProgressBarVisible) {
+        if (isAirplaneModeEnabled()) {
+            return null;
+        }
+
+        if (!mWifiManager.isWifiEnabled()) {
+            // When the airplane mode is off and Wi-Fi is disabled.
+            //   Sub-Title: Wi-Fi is off
+            if (DEBUG) {
+                Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
+        }
+
+        if (isProgressBarVisible) {
+            // When the Wi-Fi scan result callback is received
+            //   Sub-Title: Searching for networks...
+            return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
+        }
+
+        final List<ScanResult> wifiList = mWifiManager.getScanResults();
+        if (wifiList != null && wifiList.size() != 0) {
+            return mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT);
+        }
+
+        // Sub-Title:
+        // show non_carrier_network_unavailable
+        //   - while Wi-Fi on + no Wi-Fi item
+        //   - while Wi-Fi on + no Wi-Fi item + mobile data off
+        // show all_network_unavailable:
+        //   - while Wi-Fi on + no Wi-Fi item + no carrier item
+        //   - while Wi-Fi on + no Wi-Fi item + service is out of service
+        //   - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
+        if (DEBUG) {
+            Log.d(TAG, "No Wi-Fi item.");
+        }
+        if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+            if (DEBUG) {
+                Log.d(TAG, "No carrier or service is out of service.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        if (!isMobileDataEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "Mobile data off");
+            }
+            return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        if (!activeNetworkIsCellular()) {
+            if (DEBUG) {
+                Log.d(TAG, "No carrier data.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+    }
+
+    Drawable getWifiConnectedDrawable(WifiEntry wifiEntry) throws Throwable {
+        final @ColorInt int tint;
+        tint = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.colorAccentPrimaryVariant);
+        final Drawable drawable = mContext.getDrawable(
+                com.android.settingslib.Utils.getWifiIconResource(wifiEntry.getLevel()));
+        drawable.setTint(tint);
+
+        return drawable;
+    }
+
+    Drawable getSignalStrengthDrawable() {
+        Drawable drawable = mContext.getDrawable(
+                R.drawable.ic_signal_strength_zero_bar_no_internet);
+        try {
+            if (mTelephonyManager == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "TelephonyManager is null");
+                }
+                return drawable;
+            }
+
+            if (isDataStateInService() || isVoiceStateInService()) {
+                AtomicReference<Drawable> shared = new AtomicReference<>();
+                shared.set(getSignalStrengthDrawableWithLevel());
+                drawable = shared.get();
+            }
+
+            drawable.setTint(
+                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal));
+            if (activeNetworkIsCellular()) {
+                drawable.setTint(Utils.getColorAttrDefaultColor(mContext,
+                        com.android.internal.R.attr.colorAccentPrimaryVariant));
+            }
+
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+        return drawable;
+    }
+
+    /**
+     * To get the signal bar icon with level.
+     *
+     * @return The Drawable which is a signal bar icon with level.
+     */
+    Drawable getSignalStrengthDrawableWithLevel() {
+        final SignalStrength strength = mTelephonyManager.getSignalStrength();
+        int level = (strength == null) ? 0 : strength.getLevel();
+        int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+        if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+            level += 1;
+            numLevels += 1;
+        }
+        return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+    }
+
+    Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
+            int iconType, boolean cutOut) {
+        Log.d(TAG, "getSignalStrengthIcon");
+        final SignalDrawable signalDrawable = new SignalDrawable(context);
+        signalDrawable.setLevel(
+                SignalDrawable.getState(level, numLevels, cutOut));
+
+        // Make the network type drawable
+        final Drawable networkDrawable =
+                iconType == NO_CELL_DATA_TYPE_ICON
+                        ? EMPTY_DRAWABLE
+                        : context.getResources().getDrawable(iconType, context.getTheme());
+
+        // Overlay the two drawables
+        final Drawable[] layers = {networkDrawable, signalDrawable};
+        final int iconSize =
+                context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+        final LayerDrawable icons = new LayerDrawable(layers);
+        // Set the network type icon at the top left
+        icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+        // Set the signal strength icon at the bottom right
+        icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+        icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+        icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
+        return icons;
+    }
+
+    private boolean shouldInflateSignalStrength(int subId) {
+        return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
+    }
+
+    private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
+        final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
+        return displayNames.getOrDefault(subscriptionId, "");
+    }
+
+    private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
+        class DisplayInfo {
+            public SubscriptionInfo subscriptionInfo;
+            public CharSequence originalName;
+            public CharSequence uniqueName;
+        }
+
+        // Map of SubscriptionId to DisplayName
+        final Supplier<Stream<DisplayInfo>> originalInfos =
+                () -> getSubscriptionInfo()
+                        .stream()
+                        .filter(i -> {
+                            // Filter out null values.
+                            return (i != null && i.getDisplayName() != null);
+                        })
+                        .map(i -> {
+                            DisplayInfo info = new DisplayInfo();
+                            info.subscriptionInfo = i;
+                            info.originalName = i.getDisplayName().toString().trim();
+                            return info;
+                        });
+
+        // A Unique set of display names
+        Set<CharSequence> uniqueNames = new HashSet<>();
+        // Return the set of duplicate names
+        final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
+                .filter(info -> !uniqueNames.add(info.originalName))
+                .map(info -> info.originalName)
+                .collect(Collectors.toSet());
+
+        // If a display name is duplicate, append the final 4 digits of the phone number.
+        // Creates a mapping of Subscription id to original display name + phone number display name
+        final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+            if (duplicateOriginalNames.contains(info.originalName)) {
+                // This may return null, if the user cannot view the phone number itself.
+                final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
+                        info.subscriptionInfo);
+                String lastFourDigits = "";
+                if (phoneNumber != null) {
+                    lastFourDigits = (phoneNumber.length() > 4)
+                            ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
+                }
+
+                if (TextUtils.isEmpty(lastFourDigits)) {
+                    info.uniqueName = info.originalName;
+                } else {
+                    info.uniqueName = info.originalName + " " + lastFourDigits;
+                }
+
+            } else {
+                info.uniqueName = info.originalName;
+            }
+            return info;
+        });
+
+        // Check uniqueness a second time.
+        // We might not have had permission to view the phone numbers.
+        // There might also be multiple phone numbers whose last 4 digits the same.
+        uniqueNames.clear();
+        final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
+                .filter(info -> !uniqueNames.add(info.uniqueName))
+                .map(info -> info.uniqueName)
+                .collect(Collectors.toSet());
+
+        return uniqueInfos.get().map(info -> {
+            if (duplicatePhoneNames.contains(info.uniqueName)) {
+                info.uniqueName = info.originalName + " "
+                        + info.subscriptionInfo.getSubscriptionId();
+            }
+            return info;
+        }).collect(Collectors.toMap(
+                info -> info.subscriptionInfo.getSubscriptionId(),
+                info -> info.uniqueName));
+    }
+
+    CharSequence getMobileNetworkTitle() {
+        return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
+    }
+
+    String getMobileNetworkSummary() {
+        String description = getNetworkTypeDescription(mContext, mConfig,
+                mTelephonyDisplayInfo, mDefaultDataSubId);
+        return getMobileSummary(mContext, mTelephonyManager, description);
+    }
+
+    /**
+     * Get currently description of mobile network type.
+     */
+    private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
+            TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
+        String iconKey = getIconKey(telephonyDisplayInfo);
+
+        if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
+            if (DEBUG) {
+                Log.d(TAG, "The description of network type is empty.");
+            }
+            return "";
+        }
+
+        int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+        return resId != 0
+                ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
+    }
+
+    private String getMobileSummary(Context context, TelephonyManager telephonyManager,
+            String networkTypeDescription) {
+        if (!isMobileDataEnabled()) {
+            return context.getString(R.string.mobile_data_off_summary);
+        }
+        if (!isDataStateInService()) {
+            return context.getString(R.string.mobile_data_no_connection);
+        }
+        String summary = networkTypeDescription;
+        if (activeNetworkIsCellular()) {
+            summary = context.getString(R.string.preference_summary_default_combination,
+                    context.getString(R.string.mobile_data_connection_active),
+                    networkTypeDescription);
+        }
+        return summary;
+    }
+
+    String getConnectedWifiTitle() {
+        if (getConnectedWifiEntry() == null) {
+            if (DEBUG) {
+                Log.d(TAG, "connected entry is null");
+            }
+            return "";
+        }
+        return getConnectedWifiEntry().getTitle();
+    }
+
+    String getConnectedWifiSummary() {
+        if (getConnectedWifiEntry() == null) {
+            if (DEBUG) {
+                Log.d(TAG, "connected entry is null");
+            }
+            return "";
+        }
+        return getConnectedWifiEntry().getSummary(false);
+    }
+
+    void launchNetworkSetting() {
+        mCallback.dismissDialog();
+        mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+    }
+
+    void launchWifiNetworkDetailsSetting() {
+        Intent intent = getWifiDetailsSettingsIntent();
+        if (intent != null) {
+            mCallback.dismissDialog();
+            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        }
+    }
+
+    void connectCarrierNetwork() {
+        final MergedCarrierEntry mergedCarrierEntry =
+                mAccessPointController.getMergedCarrierEntry();
+        if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
+            mergedCarrierEntry.connect(null /* ConnectCallback */);
+        }
+    }
+
+    List<WifiEntry> getWifiEntryList() {
+        return mWifiEntry;
+    }
+
+    WifiEntry getConnectedWifiEntry() {
+        return mConnectedEntry;
+    }
+
+    WifiManager getWifiManager() {
+        return mWifiManager;
+    }
+
+    TelephonyManager getTelephonyManager() {
+        return mTelephonyManager;
+    }
+
+    SubscriptionManager getSubscriptionManager() {
+        return mSubscriptionManager;
+    }
+
+    /**
+     * @return whether there is the carrier item in the slice.
+     */
+    boolean hasCarrier() {
+        if (mSubscriptionManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
+            }
+            return false;
+        }
+
+        if (isAirplaneModeEnabled() || mTelephonyManager == null
+                || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Return {@code true} if mobile data is enabled
+     */
+    boolean isMobileDataEnabled() {
+        if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set whether to enable data for {@code subId}, also whether to disable data for other
+     * subscription
+     */
+    void setMobileDataEnabled(Context context, int subId, boolean enabled,
+            boolean disableOtherSubscriptions) {
+        if (mTelephonyManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
+            }
+            return;
+        }
+
+        if (mSubscriptionManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
+            }
+            return;
+        }
+
+        mTelephonyManager.setDataEnabled(enabled);
+        if (disableOtherSubscriptions) {
+            final List<SubscriptionInfo> subInfoList =
+                    mSubscriptionManager.getActiveSubscriptionInfoList();
+            if (subInfoList != null) {
+                for (SubscriptionInfo subInfo : subInfoList) {
+                    // We never disable mobile data for opportunistic subscriptions.
+                    if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
+                        context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+                                subInfo.getSubscriptionId()).setDataEnabled(false);
+                    }
+                }
+            }
+        }
+    }
+
+    boolean isDataStateInService() {
+        final ServiceState serviceState = mTelephonyManager.getServiceState();
+        NetworkRegistrationInfo regInfo =
+                (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
+                        NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        return (regInfo == null) ? false : regInfo.isRegistered();
+    }
+
+    boolean isVoiceStateInService() {
+        if (mTelephonyManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
+            }
+            return false;
+        }
+
+        final ServiceState serviceState = mTelephonyManager.getServiceState();
+        return serviceState != null
+                && serviceState.getState() == serviceState.STATE_IN_SERVICE;
+    }
+
+    boolean activeNetworkIsCellular() {
+        if (mConnectivityManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "ConnectivityManager is null, can not check active network.");
+            }
+            return false;
+        }
+
+        final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+        if (activeNetwork == null) {
+            return false;
+        }
+        final NetworkCapabilities networkCapabilities =
+                mConnectivityManager.getNetworkCapabilities(activeNetwork);
+        if (networkCapabilities == null) {
+            return false;
+        }
+        return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+    }
+
+    boolean connect(WifiEntry ap) {
+        if (ap == null) {
+            if (DEBUG) {
+                Log.d(TAG, "No Wi-Fi ap to connect.");
+            }
+            return false;
+        }
+
+        if (ap.getWifiConfiguration() != null) {
+            if (DEBUG) {
+                Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
+            }
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "connect to unsaved network " + ap.getTitle());
+            }
+        }
+        ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+        return false;
+    }
+
+    static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
+        final ActivityStarter mActivityStarter;
+        final Context mContext;
+        final WifiEntry mWifiEntry;
+
+        WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
+                WifiEntry connectWifiEntry) {
+            mActivityStarter = activityStarter;
+            mContext = context;
+            mWifiEntry = connectWifiEntry;
+        }
+
+        @Override
+        public void onConnectResult(@ConnectStatus int status) {
+            if (DEBUG) {
+                Log.d(TAG, "onConnectResult " + status);
+            }
+
+            if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
+                final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
+                        .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mActivityStarter.startActivity(intent, true);
+            } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
+                Toast.makeText(mContext, R.string.wifi_failed_connect_message,
+                        Toast.LENGTH_SHORT).show();
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "connect failure reason=" + status);
+                }
+            }
+        }
+    }
+
+    void scanWifiAccessPoints() {
+        mAccessPointController.scanForAccessPoints();
+    }
+
+    @Override
+    public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
+        if (accessPoints == null) {
+            return;
+        }
+
+        boolean hasConnectedWifi = false;
+        mWifiEntry = accessPoints;
+        for (WifiEntry wifiEntry : accessPoints) {
+            if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
+                mConnectedEntry = wifiEntry;
+                hasConnectedWifi = true;
+                break;
+            }
+        }
+        if (!hasConnectedWifi) {
+            mConnectedEntry = null;
+        }
+
+        mCallback.onAccessPointsChanged(mWifiEntry, mConnectedEntry);
+    }
+
+    @Override
+    public void onSettingsActivityTriggered(Intent settingsIntent) {
+    }
+
+    @Override
+    public void onDisconnectResult(int status) {
+    }
+
+    private class InternetTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.DataConnectionStateListener,
+            TelephonyCallback.DisplayInfoListener,
+            TelephonyCallback.ServiceStateListener,
+            TelephonyCallback.SignalStrengthsListener {
+
+        @Override
+        public void onServiceStateChanged(@NonNull ServiceState serviceState) {
+            mCallback.onServiceStateChanged(serviceState);
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            mCallback.onDataConnectionStateChanged(state, networkType);
+        }
+
+        @Override
+        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
+            mCallback.onSignalStrengthsChanged(signalStrength);
+        }
+
+        @Override
+        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+            mTelephonyDisplayInfo = telephonyDisplayInfo;
+            mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
+        }
+    }
+
+    private class InternetOnSubscriptionChangedListener
+            extends SubscriptionManager.OnSubscriptionsChangedListener {
+        InternetOnSubscriptionChangedListener() {
+            super();
+        }
+
+        @Override
+        public void onSubscriptionsChanged() {
+            updateListener();
+        }
+    }
+
+    private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
+        @Override
+        public void onCapabilitiesChanged(@NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities) {
+            final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+            if (activeNetwork != null && activeNetwork.equals(network)) {
+                // update UI
+                mCallback.onCapabilitiesChanged(network, networkCapabilities);
+            }
+        }
+    }
+
+    private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)
+                    || action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                mCallback.onWifiStateReceived(context, intent);
+            }
+
+            if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+                if (DEBUG) {
+                    Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
+                }
+                updateListener();
+            }
+        }
+    };
+
+    private void updateListener() {
+        int defaultDataSubId = getDefaultDataSubscriptionId();
+        if (mDefaultDataSubId == getDefaultDataSubscriptionId()) {
+            if (DEBUG) {
+                Log.d(TAG, "DDS: no change");
+            }
+            return;
+        }
+
+        mDefaultDataSubId = defaultDataSubId;
+        if (DEBUG) {
+            Log.d(TAG, "DDS: defaultDataSubId:" + mDefaultDataSubId);
+        }
+        if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
+            mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+            mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+            mTelephonyManager.registerTelephonyCallback(mHandler::post,
+                    mInternetTelephonyCallback);
+            mCallback.onSubscriptionsChanged(mDefaultDataSubId);
+        }
+    }
+
+    interface InternetDialogCallback {
+
+        void onRefreshCarrierInfo();
+
+        void onSimStateChanged();
+
+        void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+
+        void onSubscriptionsChanged(int defaultDataSubId);
+
+        void onServiceStateChanged(ServiceState serviceState);
+
+        void onDataConnectionStateChanged(int state, int networkType);
+
+        void onSignalStrengthsChanged(SignalStrength signalStrength);
+
+        void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
+
+        void dismissDialog();
+
+        void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry);
+
+        void onWifiStateReceived(Context context, Intent intent);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
new file mode 100644
index 0000000..e82e89ef1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tiles.dialog
+
+import android.content.Context
+import android.os.Handler
+import android.util.Log
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+private const val TAG = "InternetDialogFactory"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+/**
+ * Factory to create [InternetDialog] objects.
+ */
+@SysUISingleton
+class InternetDialogFactory @Inject constructor(
+        @Main private val handler: Handler,
+        private val internetDialogController: InternetDialogController,
+        private val context: Context,
+        private val uiEventLogger: UiEventLogger
+) {
+    companion object {
+        var internetDialog: InternetDialog? = null
+    }
+
+    /** Creates a [InternetDialog]. */
+    fun create(aboveStatusBar: Boolean, canConfigMobileData: Boolean) {
+        if (internetDialog != null) {
+            if (DEBUG) {
+                Log.d(TAG, "InternetDialog is showing, do not create it twice.")
+            }
+            return
+        } else {
+            internetDialog = InternetDialog(context, this, internetDialogController,
+                    canConfigMobileData, aboveStatusBar, uiEventLogger, handler)
+            internetDialog?.show()
+        }
+    }
+
+    fun destroyDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "destroyDialog")
+        }
+        internetDialog = null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
new file mode 100644
index 0000000..6aaba99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+public class InternetDialogUtil {
+
+    public static boolean isProviderModelEnabled(Context context) {
+        if (context == null) {
+            return false;
+        }
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index bbeff6e..77c61a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -42,7 +42,7 @@
 
     private final static String TAG = "OverviewProxyRecentsImpl";
     @Nullable
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
 
     private Context mContext;
     private Handler mHandler;
@@ -51,8 +51,8 @@
 
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
-    public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
-        mStatusBarLazy = statusBarLazy.orElse(null);
+    public OverviewProxyRecentsImpl(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
     }
 
     @Override
@@ -109,8 +109,9 @@
                 }
             };
             // Preload only if device for current user is unlocked
-            if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
-                mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
+            final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+            if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+                statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
                         // Flush trustmanager before checking device locked per user
                         mTrustManager.reportKeyguardShowingChanged();
                         mHandler.post(toggleRecents);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cb0c411..eb72296 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -34,6 +34,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -109,6 +110,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.function.BiConsumer;
+import java.util.function.Supplier;
 
 import javax.inject.Inject;
 
@@ -134,7 +136,7 @@
 
     private final Context mContext;
     private final Optional<Pip> mPipOptional;
-    private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
     private final Optional<SplitScreen> mSplitScreenOptional;
     private SysUiState mSysUiState;
@@ -171,55 +173,33 @@
     public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
         @Override
         public void startScreenPinning(int taskId) {
-            if (!verifyCaller("startScreenPinning")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    mStatusBarOptionalLazy.ifPresent(
-                            statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId,
-                                    false /* allowCancel */));
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
+                    mStatusBarOptionalLazy.get().ifPresent(
+                            statusBar -> statusBar.showScreenPinningRequest(taskId,
+                                    false /* allowCancel */)));
         }
 
         @Override
         public void stopScreenPinning() {
-            if (!verifyCaller("stopScreenPinning")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    try {
-                        ActivityTaskManager.getService().stopSystemLockTaskMode();
-                    } catch (RemoteException e) {
-                        Log.e(TAG_OPS, "Failed to stop screen pinning");
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> {
+                try {
+                    ActivityTaskManager.getService().stopSystemLockTaskMode();
+                } catch (RemoteException e) {
+                    Log.e(TAG_OPS, "Failed to stop screen pinning");
+                }
+            });
         }
 
         // TODO: change the method signature to use (boolean inputFocusTransferStarted)
         @Override
         public void onStatusBarMotionEvent(MotionEvent event) {
-            if (!verifyCaller("onStatusBarMotionEvent")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
+            verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
                 // TODO move this logic to message queue
-                mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
-                    StatusBar statusBar = statusBarLazy.get();
+                mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
                     if (event.getActionMasked() == ACTION_DOWN) {
                         statusBar.getPanelController().startExpandLatencyTracking();
                     }
-                    mHandler.post(()-> {
+                    mHandler.post(() -> {
                         int action = event.getActionMasked();
                         if (action == ACTION_DOWN) {
                             mInputFocusTransferStarted = true;
@@ -231,50 +211,38 @@
                         }
                         if (action == ACTION_UP || action == ACTION_CANCEL) {
                             mInputFocusTransferStarted = false;
+                            float velocity = (event.getY() - mInputFocusTransferStartY)
+                                    / (event.getEventTime() - mInputFocusTransferStartMillis);
                             statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
                                     action == ACTION_CANCEL,
-                                    (event.getY() - mInputFocusTransferStartY)
-                                    / (event.getEventTime() - mInputFocusTransferStartMillis));
+                                    velocity);
                         }
                         event.recycle();
                     });
                 });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            });
         }
 
         @Override
         public void onBackPressed() throws RemoteException {
-            if (!verifyCaller("onBackPressed")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
-                    sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+            verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
+                sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+                sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
 
-                    notifyBackAction(true, -1, -1, true, false);
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+                notifyBackAction(true, -1, -1, true, false);
+            });
         }
 
         @Override
         public void setHomeRotationEnabled(boolean enabled) {
-            if (!verifyCaller("setHomeRotationEnabled")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    mHandler.post(() -> notifyHomeRotationEnabled(enabled));
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
+                    mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
+        }
+
+        @Override
+        public void notifyTaskbarStatus(boolean visible, boolean stashed) {
+            verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
+                    onTaskbarStatusUpdated(visible, stashed));
         }
 
         private boolean sendEvent(int action, int code) {
@@ -291,124 +259,74 @@
 
         @Override
         public void onOverviewShown(boolean fromHome) {
-            if (!verifyCaller("onOverviewShown")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-                        mConnectionCallbacks.get(i).onOverviewShown(fromHome);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> {
+                for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+                    mConnectionCallbacks.get(i).onOverviewShown(fromHome);
+                }
+            });
         }
 
         @Override
         public Rect getNonMinimizedSplitScreenSecondaryBounds() {
-            if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
-                return null;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                return mLegacySplitScreenOptional.map(splitScreen ->
-                        splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
-                        .orElse(null);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            return verifyCallerAndClearCallingIdentity(
+                    "getNonMinimizedSplitScreenSecondaryBounds",
+                    () -> mLegacySplitScreenOptional.map(splitScreen ->
+                            splitScreen
+                                    .getDividerView()
+                                    .getNonMinimizedSplitScreenSecondaryBounds())
+                            .orElse(null)
+            );
         }
 
         @Override
         public void setNavBarButtonAlpha(float alpha, boolean animate) {
-            if (!verifyCaller("setNavBarButtonAlpha")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mNavBarButtonAlpha = alpha;
-                mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
+                    notifyNavBarButtonAlphaChanged(alpha, animate));
         }
 
         @Override
         public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
-            if (!verifyCaller("onAssistantProgress")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> notifyAssistantProgress(progress));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
+                    notifyAssistantProgress(progress));
         }
 
         @Override
         public void onAssistantGestureCompletion(float velocity) {
-            if (!verifyCaller("onAssistantGestureCompletion")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () ->
+                    notifyAssistantGestureCompletion(velocity));
         }
 
         @Override
         public void startAssistant(Bundle bundle) {
-            if (!verifyCaller("startAssistant")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> notifyStartAssistant(bundle));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("startAssistant", () ->
+                    notifyStartAssistant(bundle));
         }
 
         @Override
         public void notifyAccessibilityButtonClicked(int displayId) {
-            if (!verifyCaller("notifyAccessibilityButtonClicked")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                AccessibilityManager.getInstance(mContext)
-                        .notifyAccessibilityButtonClicked(displayId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
+                    AccessibilityManager.getInstance(mContext)
+                            .notifyAccessibilityButtonClicked(displayId));
         }
 
         @Override
         public void notifyAccessibilityButtonLongClicked() {
-            if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                final Intent intent =
-                        new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
-                final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
-                intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
+                    () -> {
+                        final Intent intent =
+                                new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+                        final String chooserClassName = AccessibilityButtonChooserActivity
+                                .class.getName();
+                        intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
+                        intent.addFlags(
+                                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    });
         }
 
         @Override
         public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
-                Insets visibleInsets, int taskId) {
+                                            Insets visibleInsets, int taskId) {
             // Deprecated
         }
 
@@ -420,43 +338,22 @@
 
         @Override
         public void notifySwipeToHomeFinished() {
-            if (!verifyCaller("notifySwipeToHomeFinished")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mPipOptional.ifPresent(
-                        pip -> pip.setPinnedStackAnimationType(
-                                PipAnimationController.ANIM_TYPE_ALPHA));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () ->
+                    mPipOptional.ifPresent(
+                            pip -> pip.setPinnedStackAnimationType(
+                                    PipAnimationController.ANIM_TYPE_ALPHA)));
         }
 
         @Override
         public void notifySwipeUpGestureStarted() {
-            if (!verifyCaller("notifySwipeUpGestureStarted")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> notifySwipeUpGestureStartedInternal());
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () ->
+                    notifySwipeUpGestureStartedInternal());
         }
 
         @Override
         public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
-            if (!verifyCaller("notifyPrioritizedRotation")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> notifyPrioritizedRotationInternal(rotation));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
+                    notifyPrioritizedRotationInternal(rotation));
         }
 
         @Override
@@ -476,15 +373,8 @@
 
         @Override
         public void expandNotificationPanel() {
-            if (!verifyCaller("expandNotificationPanel")) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            verifyCallerAndClearCallingIdentity("expandNotificationPanel",
+                    () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
         }
 
         private boolean verifyCaller(String reason) {
@@ -496,6 +386,29 @@
             }
             return true;
         }
+
+        private <T> T verifyCallerAndClearCallingIdentity(String reason, Supplier<T> supplier) {
+            if (!verifyCaller(reason)) {
+                return null;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return supplier.get();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) {
+            verifyCallerAndClearCallingIdentity(reason, () -> {
+                runnable.run();
+                return null;
+            });
+        }
+
+        private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) {
+            verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable));
+        }
     };
 
     private final Runnable mDeferredConnectionCallback = () -> {
@@ -613,7 +526,7 @@
             Optional<Pip> pipOptional,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
             Optional<SplitScreen> splitScreenOptional,
-            Optional<Lazy<StatusBar>> statusBarOptionalLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
             ShellTransitions shellTransitions,
@@ -736,12 +649,13 @@
     }
 
     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
-            boolean bouncerShowing) {
+            boolean bouncerShowing, boolean isDozing) {
         mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
                         keyguardShowing && !keyguardOccluded)
                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
                         keyguardShowing && keyguardOccluded)
                 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
+                .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
                 .commitUpdate(mContext.getDisplayId());
     }
 
@@ -766,10 +680,9 @@
     public void cleanupAfterDeath() {
         if (mInputFocusTransferStarted) {
             mHandler.post(() -> {
-                mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
+                mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
                     mInputFocusTransferStarted = false;
-                    statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
-                            0 /* velocity */);
+                    statusBar.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
                 });
             });
         }
@@ -881,6 +794,12 @@
         }
     }
 
+    private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
+        }
+    }
+
     private void notifyConnectionChanged() {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
@@ -967,19 +886,40 @@
         }
     }
 
-    public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
         try {
             if (mOverviewProxy != null) {
-                mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition,
-                        showImeSwitcher);
+                mOverviewProxy.disable(displayId, state1, state2, animate);
             } else {
-                Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status.");
+                Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
             }
         } catch (RemoteException e) {
-            Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e);
+            Log.e(TAG_OPS, "Failed to call disable()", e);
         }
+    }
 
+    public void onRotationProposal(int rotation, boolean isValid) {
+        try {
+            if (mOverviewProxy != null) {
+                mOverviewProxy.onRotationProposal(rotation, isValid);
+            } else {
+                Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
+        }
+    }
+
+    public void onSystemBarAttributesChanged(int displayId, int behavior) {
+        try {
+            if (mOverviewProxy != null) {
+                mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+            } else {
+                Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
+        }
     }
 
     private void updateEnabledState() {
@@ -1026,11 +966,10 @@
         /** Notify changes in the nav bar button alpha */
         default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
         default void onHomeRotationEnabled(boolean enabled) {}
+        default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
         default void onSystemUiStateChanged(int sysuiStateFlags) {}
         default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
         default void onAssistantGestureCompletion(float velocity) {}
         default void startAssistant(Bundle bundle) {}
-        default void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
-                int backDisposition, boolean showImeSwitcher) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index aa8d710..85bf98c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,10 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.leak.RotationUtils;
 
@@ -69,7 +69,7 @@
         NavigationModeController.ModeChangedListener {
 
     private final Context mContext;
-    private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
 
     private final AccessibilityManager mAccessibilityService;
     private final WindowManager mWindowManager;
@@ -82,7 +82,7 @@
     private int taskId;
 
     @Inject
-    public ScreenPinningRequest(Context context, Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+    public ScreenPinningRequest(Context context, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
         mContext = context;
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mAccessibilityService = (AccessibilityManager)
@@ -266,8 +266,9 @@
                         .setVisibility(View.INVISIBLE);
             }
 
-            NavigationBarView navigationBarView = mStatusBarOptionalLazy.map(
-                    statusBarLazy -> statusBarLazy.get().getNavigationBarView()).orElse(null);
+            final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+            NavigationBarView navigationBarView =
+                    statusBarOptional.map(StatusBar::getNavigationBarView).orElse(null);
             final boolean recentsVisible = navigationBarView != null
                     && navigationBarView.isRecentsButtonVisible();
             boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 1ad253e..a9ebcad 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -385,6 +385,14 @@
         });
     }
 
+    public void hideSlider() {
+        mControl.hideView();
+    }
+
+    public void showSlider() {
+        mControl.showView();
+    }
+
     private void setBrightness(float brightness) {
         mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 0ff6216..896106a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -180,6 +180,16 @@
         return mView.getValue();
     }
 
+    @Override
+    public void hideView() {
+        mView.setVisibility(View.GONE);
+    }
+
+    @Override
+    public void showView() {
+        mView.setVisibility(View.VISIBLE);
+    }
+
     private final SeekBar.OnSeekBarChangeListener mSeekListener =
             new SeekBar.OnSeekBarChangeListener() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
index a988c7a..5de22d4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
@@ -35,4 +35,7 @@
     int getMax();
     void setValue(int value);
     int getValue();
+
+    void showView();
+    void hideView();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8e52b0d..c7f8dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -50,6 +50,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -337,7 +338,7 @@
          */
         default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
                 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-                @Behavior int behavior, boolean isFullscreen) { }
+                @Behavior int behavior, InsetsState requestedState, String packageName) { }
 
         /**
          * @see IStatusBar#showTransient(int, int[]).
@@ -996,7 +997,7 @@
     @Override
     public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, boolean isFullscreen) {
+            @Behavior int behavior, InsetsState requestedState, String packageName) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.argi1 = displayId;
@@ -1004,7 +1005,8 @@
             args.argi3 = navbarColorManagedByIme ? 1 : 0;
             args.arg1 = appearanceRegions;
             args.argi4 = behavior;
-            args.argi5 = isFullscreen ? 1 : 0;
+            args.arg2 = requestedState;
+            args.arg3 = packageName;
             mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget();
         }
     }
@@ -1387,7 +1389,7 @@
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2,
                                 (AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4,
-                                args.argi5 == 1);
+                                (InsetsState) args.arg2, (String) args.arg3);
                     }
                     args.recycle();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 5a42458..b76cebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -49,11 +49,6 @@
         return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
     }
 
-    /** b/171917882 */
-    public boolean isTwoColumnNotificationShadeEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn);
-    }
-
     public boolean isKeyguardLayoutEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 538db61..8780ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -83,6 +83,35 @@
     }
 }
 
+class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
+
+    private val INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+
+    override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
+        val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
+
+        // TODO(b/193801466): add alpha reveal in the beginning as well
+        scrim.revealGradientEndColorAlpha =
+            1f - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, threshold = 0.6f)
+
+        if (isVertical) {
+            scrim.setRevealGradientBounds(
+                left = scrim.width / 2 - (scrim.width / 2) * interpolatedAmount,
+                top = 0f,
+                right = scrim.width / 2 + (scrim.width / 2) * interpolatedAmount,
+                bottom = scrim.height.toFloat()
+            )
+        } else {
+            scrim.setRevealGradientBounds(
+                left = 0f,
+                top = scrim.height / 2 - (scrim.height / 2) * interpolatedAmount,
+                right = scrim.width.toFloat(),
+                bottom = scrim.height / 2 + (scrim.height / 2) * interpolatedAmount
+            )
+        }
+    }
+}
+
 class CircleReveal(
     /** X-value of the circle center of the reveal. */
     val centerX: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 6f4a73e..f0da702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -60,7 +60,6 @@
     private val mediaHierarchyManager: MediaHierarchyManager,
     private val scrimController: ScrimController,
     private val depthController: NotificationShadeDepthController,
-    private val featureFlags: FeatureFlags,
     private val context: Context,
     configurationController: ConfigurationController,
     falsingManager: FalsingManager
@@ -134,7 +133,7 @@
                 R.dimen.lockscreen_shade_scrim_transition_distance)
         fullTransitionDistance = context.resources.getDimensionPixelSize(
                 R.dimen.lockscreen_shade_qs_transition_distance)
-        useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+        useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
     }
 
     fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index e7e9404..c681a44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -285,6 +285,7 @@
         internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
 
+        mCurrentUserId = ActivityManager.getCurrentUser(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
 
         mSettingsObserver.onChange(false);  // set up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 25cbdc5..8969b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -84,6 +84,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 
 import dagger.Lazy;
@@ -132,7 +133,7 @@
     private final Context mContext;
     private final MediaSessionManager mMediaSessionManager;
     private final ArrayList<MediaListener> mMediaListeners;
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final MediaArtworkProcessor mMediaArtworkProcessor;
     private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
 
@@ -177,7 +178,7 @@
      */
     public NotificationMediaManager(
             Context context,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
@@ -197,7 +198,7 @@
         mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(
                 Context.MEDIA_SESSION_SERVICE);
         // TODO: use KeyguardStateController#isOccluded to remove this dependency
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mNotificationShadeWindowController = notificationShadeWindowController;
         mEntryManager = notificationEntryManager;
         mMainExecutor = mainExecutor;
@@ -694,7 +695,8 @@
 
         NotificationShadeWindowController windowController =
                 mNotificationShadeWindowController.get();
-        boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
+        boolean hideBecauseOccluded =
+                mStatusBarOptionalLazy.get().map(StatusBar::isOccluded).orElse(false);
 
         final boolean hasArtwork = artworkDrawable != null;
         mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 4552138..bc3883c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -45,6 +45,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.RemoteViews;
+import android.widget.RemoteViews.InteractionHandler;
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Stream;
 
@@ -118,7 +120,7 @@
     private final Handler mMainHandler;
     private final ActionClickLogger mLogger;
 
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
 
     protected final Context mContext;
     private final UserManager mUserManager;
@@ -134,14 +136,14 @@
     protected Callback mCallback;
     protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
 
-    private final RemoteViews.InteractionHandler
-            mInteractionHandler = new RemoteViews.InteractionHandler() {
+    private final InteractionHandler mInteractionHandler = new InteractionHandler() {
 
         @Override
         public boolean onInteraction(
                 View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
-            mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
-                    "NOTIFICATION_CLICK");
+            mStatusBarOptionalLazy.get().ifPresent(
+                    statusBar -> statusBar.wakeUpIfDozing(
+                            SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"));
 
             final NotificationEntry entry = getNotificationForParent(view.getParent());
             mLogger.logInitialClick(entry, pendingIntent);
@@ -280,7 +282,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             SmartReplyController smartReplyController,
             NotificationEntryManager notificationEntryManager,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             StatusBarStateController statusBarStateController,
             @Main Handler mainHandler,
             RemoteInputUriController remoteInputUriController,
@@ -290,7 +292,7 @@
         mLockscreenUserManager = lockscreenUserManager;
         mSmartReplyController = smartReplyController;
         mEntryManager = notificationEntryManager;
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mMainHandler = mainHandler;
         mLogger = logger;
         mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cd5cce4..3bd7dd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -183,7 +183,6 @@
             }
 
             viewState.hidden = !mAmbientState.isShadeExpanded()
-                    || mAmbientState.isQsCustomizerShowing()
                     || algorithmState.firstViewInShelf == null;
 
             final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0725bf9..d4f5bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar;
 
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
 
@@ -23,10 +27,16 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.os.SystemProperties;
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.view.InsetsFlags;
+import android.view.InsetsState;
 import android.view.View;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
@@ -56,6 +66,9 @@
 public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
         CallbackController<StateListener>, Dumpable {
     private static final String TAG = "SbStateController";
+    private static final boolean DEBUG_IMMERSIVE_APPS =
+            SystemProperties.getBoolean("persist.debug.immersive_apps", false);
+
     // Must be a power of 2
     private static final int HISTORY_SIZE = 32;
 
@@ -420,7 +433,10 @@
     }
 
     @Override
-    public void setFullscreenState(boolean isFullscreen) {
+    public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+            InsetsState requestedState, String packageName) {
+        boolean isFullscreen = !requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+                || !requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
         if (mIsFullscreen != isFullscreen) {
             mIsFullscreen = isFullscreen;
             synchronized (mListeners) {
@@ -429,6 +445,19 @@
                 }
             }
         }
+
+        // TODO (b/190543382): Finish the logging logic.
+        // This section can be removed if we don't need to print it on logcat.
+        if (DEBUG_IMMERSIVE_APPS) {
+            boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
+            String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
+            String requestedVisibilityString = requestedState.toSourceVisibilityString();
+            if (requestedVisibilityString.isEmpty()) {
+                requestedVisibilityString = "none";
+            }
+            Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
+                    + " requested visibilities: " + requestedVisibilityString);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2520050..0bbae2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,7 +19,10 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.view.InsetsState;
 import android.view.View;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -155,9 +158,10 @@
     boolean isKeyguardRequested();
 
     /**
-     * Set the fullscreen state
+     * Set the system bar attributes
      */
-    void setFullscreenState(boolean isFullscreen);
+    void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+            InsetsState requestedState, String packageName);
 
     /**
      * Set pulsing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 4919593..a05e950 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -27,6 +27,7 @@
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -92,7 +93,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             SmartReplyController smartReplyController,
             NotificationEntryManager notificationEntryManager,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             StatusBarStateController statusBarStateController,
             Handler mainHandler,
             RemoteInputUriController remoteInputUriController,
@@ -103,7 +104,7 @@
                 lockscreenUserManager,
                 smartReplyController,
                 notificationEntryManager,
-                statusBarLazy,
+                statusBarOptionalLazy,
                 statusBarStateController,
                 mainHandler,
                 remoteInputUriController,
@@ -116,7 +117,7 @@
     @Provides
     static NotificationMediaManager provideNotificationMediaManager(
             Context context,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
@@ -129,7 +130,7 @@
             MediaDataManager mediaDataManager) {
         return new NotificationMediaManager(
                 context,
-                statusBarLazy,
+                statusBarOptionalLazy,
                 notificationShadeWindowController,
                 notificationEntryManager,
                 mediaArtworkProcessor,
@@ -253,4 +254,9 @@
         ongoingCallController.init();
         return ongoingCallController;
     }
+
+    /** */
+    @Binds
+    QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
+            QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 29cfb07..19a00d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -70,7 +70,7 @@
     private val configurationController: ConfigurationController,
     private val contentInsetsProvider: StatusBarContentInsetsProvider,
     private val animationScheduler: SystemStatusAnimationScheduler
-) {
+) : StatusBarContentInsetsChangedListener {
     private var sbHeightPortrait = 0
     private var sbHeightLandscape = 0
 
@@ -98,12 +98,7 @@
         get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
 
     init {
-        contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener {
-            override fun onStatusBarContentInsetsChanged() {
-                dlog("onStatusBarContentInsetsChanged: ")
-                setNewLayoutRects()
-            }
-        })
+        contentInsetsProvider.addCallback(this)
         configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
             override fun onLayoutDirectionChanged(isRtl: Boolean) {
                 synchronized(this) {
@@ -477,7 +472,7 @@
     private fun resolveState(state: ViewState) {
         dlog("resolveState $state")
         if (!state.viewInitialized) {
-            dlog("resolveState: view is not initialized. skipping.")
+            dlog("resolveState: view is not initialized. skipping")
             return
         }
 
@@ -538,6 +533,11 @@
         return -1
     }
 
+    override fun onStatusBarContentInsetsChanged() {
+        Log.d(TAG, "onStatusBarContentInsetsChanged: ")
+        setNewLayoutRects()
+    }
+
     // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
     private fun getLayoutRects(): List<Rect> {
         val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 94ee868..6369a7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -127,11 +127,8 @@
     public int targetSdk;
     private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
     public CharSequence remoteInputText;
-    // Mimetype and Uri used to display the image in the notification *after* it has been sent.
     public String remoteInputMimeType;
     public Uri remoteInputUri;
-    // ContentInfo used to keep the attachment permission alive until RemoteInput is sent or
-    // cancelled.
     public ContentInfo remoteInputAttachment;
     private Notification.BubbleMetadata mBubbleMetadata;
     private ShortcutInfo mShortcutInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6964838..f758114 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -126,7 +126,7 @@
     @Provides
     static NotificationGutsManager provideNotificationGutsManager(
             Context context,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             @Main Handler mainHandler,
             @Background Handler bgHandler,
             AccessibilityManager accessibilityManager,
@@ -145,7 +145,7 @@
             ShadeController shadeController) {
         return new NotificationGutsManager(
                 context,
-                statusBarLazy,
+                statusBarOptionalLazy,
                 mainHandler,
                 bgHandler,
                 accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 4319e29..8fe0894 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -116,7 +116,7 @@
     @VisibleForTesting
     protected String mKeyToRemoveOnGutsClosed;
 
-    private final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final Handler mMainHandler;
     private final Handler mBgHandler;
     private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -135,7 +135,7 @@
      * Injected constructor. See {@link NotificationsModule}.
      */
     public NotificationGutsManager(Context context,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             @Main Handler mainHandler,
             @Background Handler bgHandler,
             AccessibilityManager accessibilityManager,
@@ -153,7 +153,7 @@
             OnUserInteractionCallback onUserInteractionCallback,
             ShadeController shadeController) {
         mContext = context;
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mMainHandler = mainHandler;
         mBgHandler = bgHandler;
         mAccessibilityManager = accessibilityManager;
@@ -561,17 +561,22 @@
                             .setLeaveOpenOnKeyguardHide(true);
                 }
 
-                Runnable r = () -> mMainHandler.post(
-                        () -> openGutsInternal(view, x, y, menuItem));
-
-                mStatusBarLazy.get().executeRunnableDismissingKeyguard(
-                        r,
-                        null /* cancelAction */,
-                        false /* dismissShade */,
-                        true /* afterKeyguardGone */,
-                        true /* deferred */);
-
-                return true;
+                Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+                if (statusBarOptional.isPresent()) {
+                    Runnable r = () -> mMainHandler.post(
+                            () -> openGutsInternal(view, x, y, menuItem));
+                    statusBarOptional.get().executeRunnableDismissingKeyguard(
+                            r,
+                            null /* cancelAction */,
+                            false /* dismissShade */,
+                            true /* afterKeyguardGone */,
+                            true /* deferred */);
+                    return true;
+                }
+                /**
+                 * When {@link StatusBar} doesn't exist, falling through to call
+                 * {@link #openGutsInternal(View,int,int,NotificationMenuRowPlugin.MenuItem)}.
+                 */
             }
         }
         return openGutsInternal(view, x, y, menuItem);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 9846b28..4559e13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,7 +72,6 @@
     private boolean mPanelFullWidth;
     private boolean mPulsing;
     private boolean mUnlockHintRunning;
-    private boolean mQsCustomizerShowing;
     private int mIntrinsicPadding;
     private float mHideAmount;
     private boolean mAppearing;
@@ -503,14 +502,6 @@
         return mUnlockHintRunning;
     }
 
-    public boolean isQsCustomizerShowing() {
-        return mQsCustomizerShowing;
-    }
-
-    public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
-        mQsCustomizerShowing = qsCustomizerShowing;
-    }
-
     public void setIntrinsicPadding(int intrinsicPadding) {
         mIntrinsicPadding = intrinsicPadding;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4ad7202..50811c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -81,7 +81,6 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -89,7 +88,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
@@ -112,7 +110,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.util.Assert;
-import com.android.systemui.util.leak.RotationUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -156,7 +153,7 @@
      * gap is drawn between them). In this case we don't want to round their corners.
      */
     private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
-    private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
+    private boolean mKeyguardBypassEnabled;
 
     private ExpandHelper mExpandHelper;
     private NotificationSwipeHelper mSwipeHelper;
@@ -213,7 +210,6 @@
 
     private GroupMembershipManager mGroupMembershipManager;
     private GroupExpansionManager mGroupExpansionManager;
-    private NotificationActivityStarter mNotificationActivityStarter;
     private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
     private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
     private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
@@ -413,6 +409,7 @@
     private boolean mBackwardScrollable;
     private NotificationShelf mShelf;
     private int mMaxDisplayedNotifications = -1;
+    private float mKeyguardBottomPadding = -1;
     private int mStatusBarHeight;
     private int mMinInteractionHeight;
     private final Rect mClipRect = new Rect();
@@ -530,7 +527,6 @@
     private NotificationEntry mTopHeadsUpEntry;
     private long mNumHeadsUp;
     private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
-    private final FeatureFlags mFeatureFlags;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private boolean mShouldUseSplitNotificationShade;
 
@@ -565,6 +561,9 @@
         }
     };
 
+    @Nullable
+    private OnClickListener mManageButtonClickListener;
+
     @Inject
     public NotificationStackScrollLayout(
             @Named(VIEW_CONTEXT) Context context,
@@ -573,12 +572,10 @@
             GroupMembershipManager groupMembershipManager,
             GroupExpansionManager groupExpansionManager,
             AmbientState ambientState,
-            FeatureFlags featureFlags,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
         mSectionsManager = notificationSectionsManager;
-        mFeatureFlags = featureFlags;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         updateSplitNotificationShade();
         mSectionsManager.initialize(this, LayoutInflater.from(context));
@@ -651,12 +648,20 @@
     }
 
     /**
+     * Sets whether keyguard bypass is enabled. If true, this layout will be rendered in bypass
+     * mode when it is on the keyguard.
+     */
+    public void setKeyguardBypassEnabled(boolean isEnabled) {
+        mKeyguardBypassEnabled = isEnabled;
+    }
+
+    /**
      * @return the height at which we will wake up when pulsing
      */
     public float getWakeUpHeight() {
         ExpandableView firstChild = getFirstChildWithBackground();
         if (firstChild != null) {
-            if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+            if (mKeyguardBypassEnabled) {
                 return firstChild.getHeadsUpHeightWithoutHeader();
             } else {
                 return firstChild.getCollapsedHeight();
@@ -737,6 +742,16 @@
             mDebugPaint.setColor(Color.YELLOW);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
 
+            y = (int) mMaxLayoutHeight;
+            mDebugPaint.setColor(Color.MAGENTA);
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+            if (mKeyguardBottomPadding >= 0) {
+                y = getHeight() - (int) mKeyguardBottomPadding;
+                mDebugPaint.setColor(Color.GRAY);
+                canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            }
+
             y = getHeight() - getEmptyBottomMargin();
             mDebugPaint.setColor(Color.GREEN);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -784,7 +799,7 @@
             }
         }
         boolean shouldDrawBackground;
-        if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+        if (mKeyguardBypassEnabled && onKeyguard()) {
             shouldDrawBackground = isPulseExpanding();
         } else {
             shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
@@ -899,15 +914,12 @@
     }
 
     private void reinitView() {
-        initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper);
+        initView(getContext(), mSwipeHelper);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void initView(Context context,
-            KeyguardBypassEnabledProvider keyguardBypassEnabledProvider,
-            NotificationSwipeHelper swipeHelper) {
+    void initView(Context context, NotificationSwipeHelper swipeHelper) {
         mScroller = new OverScroller(getContext());
-        mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider;
         mSwipeHelper = swipeHelper;
 
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -949,7 +961,7 @@
             return;
         }
         // Portrait is easy, just use the dimen for paddings
-        if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
             mSidePaddings = mMinimumPaddings;
             return;
         }
@@ -1347,7 +1359,7 @@
     private void notifyAppearChangedListeners() {
         float appear;
         float expandAmount;
-        if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+        if (mKeyguardBypassEnabled && onKeyguard()) {
             appear = calculateAppearFractionBypass();
             expandAmount = getPulseHeight();
         } else {
@@ -2385,8 +2397,7 @@
             minTopPosition = firstVisibleSection.getBounds().top;
         }
         boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
-                && (mAmbientState.isDozing()
-                        || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
+                && (mAmbientState.isDozing() || (mKeyguardBypassEnabled && onKeyguard));
         for (NotificationSection section : mSections) {
             int minBottomPosition = minTopPosition;
             if (section == lastSection) {
@@ -2529,7 +2540,7 @@
         } else {
             mTopPaddingOverflow = 0;
         }
-        setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled());
+        setTopPadding(topPadding, animate && !mKeyguardBypassEnabled);
         setExpandedHeight(mExpandedHeight);
     }
 
@@ -3093,7 +3104,7 @@
             boolean performDisappearAnimation = !mIsExpanded
                     // Only animate if we still have pinned heads up, otherwise we just have the
                     // regular collapse animation of the lock screen
-                    || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
+                    || (mKeyguardBypassEnabled && onKeyguard()
                             && mInHeadsUpPinnedMode);
             if (performDisappearAnimation && !isHeadsUp) {
                 type = row.wasJustClicked()
@@ -4316,7 +4327,7 @@
         // Since we are clipping to the outline we need to make sure that the shadows aren't
         // clipped when pulsing
         float ownTranslationZ = 0;
-        if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
+        if (mKeyguardBypassEnabled && mAmbientState.isHiddenAtAll()) {
             ExpandableView firstChildNotGone = getFirstChildNotGone();
             if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
                 ownTranslationZ = firstChildNotGone.getTranslationZ();
@@ -4358,6 +4369,14 @@
         return -1;
     }
 
+    /**
+     * Returns whether or not a History button is shown in the footer. If there is no footer, then
+     * this will return false.
+     **/
+    public boolean isHistoryShown() {
+        return mFooterView != null && mFooterView.isHistoryShown();
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     void setFooterView(@NonNull FooterView footerView) {
         int index = -1;
@@ -4367,6 +4386,9 @@
         }
         mFooterView = footerView;
         addView(mFooterView, index);
+        if (mManageButtonClickListener != null) {
+            mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4772,6 +4794,16 @@
         }
     }
 
+    /**
+     * This is used for debugging only; it will be used to draw the otherwise invisible line which
+     * NotificationPanelViewController treats as the bottom when calculating how many notifications
+     * appear on the keyguard.
+     * Setting a negative number will disable rendering this line.
+     */
+    public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+        mKeyguardBottomPadding = keyguardBottomPadding;
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
         mShouldShowShelfOnly = shouldShowShelfOnly;
@@ -4854,25 +4886,18 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setQsCustomizerShowing(boolean isShowing) {
-        mAmbientState.setQsCustomizerShowing(isShowing);
-        requestChildrenUpdate();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
         mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+        pw.println(String.format("[%s: pulsing=%s visibility=%s"
                         + " alpha=%f scrollY:%d maxTopPadding=%d showShelfOnly=%s"
                         + " qsExpandFraction=%f"
                         + " hideAmount=%f]",
                 this.getClass().getSimpleName(),
                 mPulsing ? "T" : "f",
-                mAmbientState.isQsCustomizerShowing() ? "T" : "f",
                 getVisibility() == View.VISIBLE ? "visible"
                         : getVisibility() == View.GONE ? "gone"
                                 : "invisible",
@@ -5070,9 +5095,12 @@
         }
     }
 
-    public void setNotificationActivityStarter(
-            NotificationActivityStarter notificationActivityStarter) {
-        mNotificationActivityStarter = notificationActivityStarter;
+    /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
+    public void setManageButtonClickListener(@Nullable OnClickListener listener) {
+        mManageButtonClickListener = listener;
+        if (mFooterView != null) {
+            mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+        }
     }
 
     @VisibleForTesting
@@ -5086,9 +5114,6 @@
             }
             clearNotifications(ROWS_ALL, true /* closeShade */);
         });
-        footerView.setManageButtonClickListener(v -> {
-            mNotificationActivityStarter.startHistoryIntent(v, mFooterView.isHistoryShown());
-        });
         setFooterView(footerView);
     }
 
@@ -5139,7 +5164,7 @@
      */
     public float setPulseHeight(float height) {
         mAmbientState.setPulseHeight(height);
-        if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+        if (mKeyguardBypassEnabled) {
             notifyAppearChangedListeners();
         }
         requestChildrenUpdate();
@@ -5395,7 +5420,7 @@
     }
 
     private void updateSplitNotificationShade() {
-        boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+        boolean split = shouldUseSplitNotificationShade(getResources());
         if (split != mShouldUseSplitNotificationShade) {
             mShouldUseSplitNotificationShade = split;
             updateDismissBehavior();
@@ -5642,6 +5667,10 @@
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
+    boolean isUsingSplitNotificationShade() {
+        return mShouldUseSplitNotificationShade;
+    }
+
     static boolean matchesSelection(
             ExpandableNotificationRow row,
             @SelectedRows int selection) {
@@ -6066,10 +6095,6 @@
     /** Only rows where entry.isHighPriority() is false. */
     public static final int ROWS_GENTLE = 2;
 
-    interface KeyguardBypassEnabledProvider {
-        boolean getBypassEnabled();
-    }
-
     interface DismissListener {
         void onDismiss(@SelectedRows int selectedRows);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 09afedb..8ee0cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -48,6 +48,8 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.jank.InteractionJankMonitor;
@@ -184,6 +186,9 @@
     private final NotificationListContainerImpl mNotificationListContainer =
             new NotificationListContainerImpl();
 
+    @Nullable
+    private NotificationActivityStarter mNotificationActivityStarter;
+
     private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
 
     /**
@@ -708,8 +713,15 @@
             });
         }
 
-        mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
-                mSwipeHelper);
+        mView.initView(mView.getContext(), mSwipeHelper);
+        mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
+        mKeyguardBypassController
+                .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
+        mView.setManageButtonClickListener(v -> {
+            if (mNotificationActivityStarter != null) {
+                mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
+            }
+        });
 
         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
@@ -1116,11 +1128,16 @@
     /**
      * Update whether we should show the empty shade view (no notifications in the shade).
      * If so, send the update to our view.
+     *
+     * When in split mode, notifications are always visible regardless of the state of the
+     * QuickSettings panel. That being the case, empty view is always shown if the other conditions
+     * are true.
      */
     public void updateShowEmptyShadeView() {
         mShowEmptyShadeView = mBarState != KEYGUARD
-                && !mView.isQsExpanded()
+                && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
                 && mView.getVisibleNotificationCount() == 0;
+
         mView.updateEmptyShadeView(
                 mShowEmptyShadeView,
                 mZenModeController.areNotificationsHiddenInShade());
@@ -1199,6 +1216,16 @@
         mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
     }
 
+    /**
+     * This is used for debugging only; it will be used to draw the otherwise invisible line which
+     * NotificationPanelViewController treats as the bottom when calculating how many notifications
+     * appear on the keyguard.
+     * Setting a negative number will disable rendering this line.
+     */
+    public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+        mView.setKeyguardBottomPadding(keyguardBottomPadding);
+    }
+
     public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationEntry entry,
@@ -1465,6 +1492,10 @@
         mView.animateNextTopPaddingChange();
     }
 
+    public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
+        mNotificationActivityStarter = activityStarter;
+    }
+
     /**
      * Enum for UiEvent logged from this class
      */
@@ -1536,7 +1567,8 @@
         @Override
         public void setNotificationActivityStarter(
                 NotificationActivityStarter notificationActivityStarter) {
-            mView.setNotificationActivityStarter(notificationActivityStarter);
+            NotificationStackScrollLayoutController.this
+                    .setNotificationActivityStarter(notificationActivityStarter);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d0507e1..b36dc56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -274,7 +274,9 @@
         // expanded. Consider updating these states in updateContentView instead so that we don't
         // have to recalculate in every frame.
         float currentY = -ambientState.getScrollY();
-        if (!ambientState.isOnKeyguard()) {
+        if (!ambientState.isOnKeyguard()
+                || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
+            // add top padding at the start as long as we're not on the lock screen
             currentY += mNotificationScrimPadding;
         }
         state.firstViewInShelf = null;
@@ -324,7 +326,8 @@
      */
     private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
-        if (!ambientState.isOnKeyguard()) {
+        if (!ambientState.isOnKeyguard()
+                || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
             algorithmState.mCurrentYPosition += mNotificationScrimPadding;
             algorithmState.mCurrentExpandedYPosition += mNotificationScrimPadding;
         }
@@ -355,7 +358,9 @@
                 && algorithmState.firstViewInShelf != null;
 
         final float shelfHeight = showingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f;
-        final float scrimPadding = ambientState.isOnKeyguard() ? 0 : mNotificationScrimPadding;
+        final float scrimPadding = ambientState.isOnKeyguard()
+                && (!ambientState.isBypassEnabled() || !ambientState.isPulseExpanding())
+                ? 0 : mNotificationScrimPadding;
 
         final float stackHeight = ambientState.getStackHeight()  - shelfHeight - scrimPadding;
         final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 596fce5..14b39fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -57,9 +57,12 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Contains the collapsed status bar and handles hiding/showing based on disable flags
  * and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -85,7 +88,7 @@
     private View mCenteredIconArea;
     private int mDisabled1;
     private int mDisabled2;
-    private final StatusBar mStatusBarComponent;
+    private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private DarkIconManager mDarkIconManager;
     private View mOperatorNameFrame;
     private final CommandQueue mCommandQueue;
@@ -123,7 +126,7 @@
             KeyguardStateController keyguardStateController,
             NetworkController networkController,
             StatusBarStateController statusBarStateController,
-            StatusBar statusBarComponent,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             CommandQueue commandQueue
     ) {
         mOngoingCallController = ongoingCallController;
@@ -135,7 +138,7 @@
         mKeyguardStateController = keyguardStateController;
         mNetworkController = networkController;
         mStatusBarStateController = statusBarStateController;
-        mStatusBarComponent = statusBarComponent;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mCommandQueue = commandQueue;
     }
 
@@ -271,7 +274,8 @@
     }
 
     protected int adjustDisableFlags(int state) {
-        boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+        boolean headsUpVisible = mStatusBarOptionalLazy.get()
+                .map(StatusBar::headsUpShouldBeVisible).orElse(false);
         if (headsUpVisible) {
             state |= DISABLE_CLOCK;
         }
@@ -299,7 +303,8 @@
         // The shelf will be hidden when dozing with a custom clock, we must show notification
         // icons in this occasion.
         if (mStatusBarStateController.isDozing()
-                && mStatusBarComponent.getPanelController().hasCustomClock()) {
+                && mStatusBarOptionalLazy.get().map(
+                        sb -> sb.getPanelController().hasCustomClock()).orElse(false)) {
             state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
         }
 
@@ -340,10 +345,13 @@
     }
 
     private boolean shouldHideNotificationIcons() {
-        if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) {
+        final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+        if (!mStatusBar.isClosed()
+                && statusBarOptional.map(
+                        StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
             return true;
         }
-        if (mStatusBarComponent.hideStatusBarIconsForBouncer()) {
+        if (statusBarOptional.map(StatusBar::hideStatusBarIconsForBouncer).orElse(false)) {
             return true;
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index ec66971..6caeba2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -43,8 +43,13 @@
     @BypassOverride private val bypassOverride: Int
     private var hasFaceFeature: Boolean
     private var pendingUnlock: PendingUnlock? = null
+    private val listeners = mutableListOf<OnBypassStateChangedListener>()
     var userHasDeviceEntryIntent: Boolean = false // ie: attempted udfps auth
 
+    private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
+        override fun onFaceAuthEnabledChanged() = notifyListeners()
+    }
+
     @IntDef(
         FACE_UNLOCK_BYPASS_NO_OVERRIDE,
         FACE_UNLOCK_BYPASS_ALWAYS,
@@ -84,7 +89,10 @@
             }
             return enabled && mKeyguardStateController.isFaceAuthEnabled
         }
-        private set
+        private set(value) {
+            field = value
+            notifyListeners()
+        }
 
     var bouncerShowing: Boolean = false
     var altBouncerShowing: Boolean = false
@@ -141,6 +149,8 @@
                 })
     }
 
+    private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
+
     /**
      * Notify that the biometric unlock has happened.
      *
@@ -224,6 +234,32 @@
         pw.println("  hasFaceFeature: $hasFaceFeature")
     }
 
+    /** Registers a listener for bypass state changes. */
+    fun registerOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+        val start = listeners.isEmpty()
+        listeners.add(listener)
+        if (start) {
+            mKeyguardStateController.addCallback(faceAuthEnabledChangedCallback)
+        }
+    }
+
+    /**
+     * Unregisters a listener for bypass state changes, previous registered with
+     * [registerOnBypassStateChangedListener]
+     */
+    fun unregisterOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+        listeners.remove(listener)
+        if (listeners.isEmpty()) {
+            mKeyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
+        }
+    }
+
+    /** Listener for bypass state change events.  */
+    interface OnBypassStateChangedListener {
+        /** Invoked when bypass becomes enabled or disabled. */
+        fun onBypassStateChanged(isEnabled: Boolean)
+    }
+
     companion object {
         const val BYPASS_FADE_DURATION = 67
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 7d13405..c213707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -21,6 +21,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.Nullable;
+import android.view.InsetsState;
 import android.view.View;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -149,7 +150,7 @@
         @Override
         public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
                 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-                @Behavior int behavior, boolean isFullscreen) {
+                @Behavior int behavior, InsetsState requestedState, String packageName) {
             if (displayId != mDisplayId) {
                 return;
             }
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 3c1892d..9d94056 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -21,11 +21,16 @@
 import static androidx.constraintlayout.widget.ConstraintSet.END;
 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
 import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 
 import static java.lang.Float.isNaN;
@@ -58,6 +63,8 @@
 import android.os.UserManager;
 import android.os.VibrationEffect;
 import android.provider.Settings;
+import android.transition.ChangeBounds;
+import android.transition.TransitionManager;
 import android.util.Log;
 import android.util.MathUtils;
 import android.view.LayoutInflater;
@@ -116,7 +123,6 @@
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -317,7 +323,6 @@
     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
     private final QSDetailDisplayer mQSDetailDisplayer;
     private final FragmentService mFragmentService;
-    private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
     private final PrivacyDotViewController mPrivacyDotViewController;
     private final QuickAccessWalletController mQuickAccessWalletController;
@@ -328,8 +333,11 @@
     private final int mMaxKeyguardNotifications;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final TapAgainViewController mTapAgainViewController;
+    private final SplitShadeHeaderController mSplitShadeHeaderController;
     private final RecordingController mRecordingController;
     private boolean mShouldUseSplitNotificationShade;
+    // The bottom padding reserved for elements of the keyguard measuring notifications
+    private float mKeyguardNotificationBottomPadding;
     // Current max allowed keyguard notifications determined by measuring the panel
     private int mMaxAllowedKeyguardNotifications;
 
@@ -391,7 +399,7 @@
     private float mDownY;
     private int mDisplayTopInset = 0; // in pixels
     private int mDisplayRightInset = 0; // in pixels
-    private int mSplitShadeNotificationsTopPadding;
+    private int mSplitShadeStatusBarHeight;
 
     private final KeyguardClockPositionAlgorithm
             mClockPositionAlgorithm =
@@ -631,6 +639,8 @@
 
     private KeyguardMediaController mKeyguardMediaController;
 
+    private boolean mStatusViewCentered = false;
+
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -705,7 +715,6 @@
             NotificationShadeDepthController notificationShadeDepthController,
             AmbientState ambientState,
             LockIconViewController lockIconViewController,
-            FeatureFlags featureFlags,
             KeyguardMediaController keyguardMediaController,
             PrivacyDotViewController privacyDotViewController,
             TapAgainViewController tapAgainViewController,
@@ -716,6 +725,7 @@
             RecordingController recordingController,
             @Main Executor uiExecutor,
             SecureSettings secureSettings,
+            SplitShadeHeaderController splitShadeHeaderController,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             NotificationRemoteInputManager remoteInputManager) {
         super(view, falsingManager, dozeLog, keyguardStateController,
@@ -739,7 +749,6 @@
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
         mDepthController = notificationShadeDepthController;
-        mFeatureFlags = featureFlags;
         mContentResolver = contentResolver;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
@@ -747,8 +756,9 @@
         mFragmentService = fragmentService;
         mSettingsChangeObserver = new SettingsChangeObserver(handler);
         mShouldUseSplitNotificationShade =
-                Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
+                Utils.shouldUseSplitNotificationShade(mResources);
         mView.setWillNotDraw(!DEBUG);
+        mSplitShadeHeaderController = splitShadeHeaderController;
         mLayoutInflater = layoutInflater;
         mFalsingManager = falsingManager;
         mFalsingCollector = falsingCollector;
@@ -937,8 +947,6 @@
                 R.dimen.notification_panel_min_side_margin);
         mIndicationBottomPadding = mResources.getDimensionPixelSize(
                 R.dimen.keyguard_indication_bottom_padding);
-        mQsNotificationTopPadding = mResources.getDimensionPixelSize(
-                R.dimen.qs_notification_padding);
         mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
         mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
         int statusbarHeight = mResources.getDimensionPixelSize(
@@ -1018,16 +1026,21 @@
     public void updateResources() {
         mQuickQsOffsetHeight = mResources.getDimensionPixelSize(
                 com.android.internal.R.dimen.quick_qs_offset_height);
-        mSplitShadeNotificationsTopPadding =
-                mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
+        mSplitShadeStatusBarHeight =
+                mResources.getDimensionPixelSize(R.dimen.split_shade_header_height);
         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
         mShouldUseSplitNotificationShade =
-                Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
+                Utils.shouldUseSplitNotificationShade(mResources);
         mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
         if (mQs != null) {
             mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
         }
+
+        int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
+                mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
+        mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
+
         // To change the constraints at runtime, all children of the ConstraintLayout must have ids
         ensureAllViewsHaveIds(mNotificationContainerParent);
         ConstraintSet constraintSet = new ConstraintSet();
@@ -1040,15 +1053,18 @@
             constraintSet.connect(
                     R.id.notification_stack_scroller, START,
                     R.id.qs_edge_guideline, START);
-            constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END);
         } else {
             constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
             constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
-            constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END);
         }
         constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
         constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
+        constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
+        constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
         constraintSet.applyTo(mNotificationContainerParent);
+        mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
+
+        updateKeyguardStatusViewAlignment(false /* animate */);
 
         mKeyguardMediaController.refreshMediaPosition();
     }
@@ -1187,9 +1203,12 @@
         if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
                     mMaxAllowedKeyguardNotifications);
+            mNotificationStackScrollLayoutController.setKeyguardBottomPadding(
+                    mKeyguardNotificationBottomPadding);
         } else {
             // no max when not on the keyguard
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+            mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f);
         }
     }
 
@@ -1267,7 +1286,14 @@
             updateClockAppearance();
         }
         if (!onKeyguard) {
-            stackScrollerPadding = getUnlockedStackScrollerPadding();
+            if (mShouldUseSplitNotificationShade) {
+                // Quick settings are not on the top of the notifications
+                // when in split shade mode (they are on the left side),
+                // so we should not add a padding for them
+                stackScrollerPadding = 0;
+            } else {
+                stackScrollerPadding = getUnlockedStackScrollerPadding();
+            }
         } else {
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
         }
@@ -1288,7 +1314,13 @@
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
         final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
                 .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
-        mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+        if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
+                || (mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia())) {
+            mKeyguardStatusViewController.displayClock(SMALL);
+        } else {
+            mKeyguardStatusViewController.displayClock(LARGE);
+        }
+        updateKeyguardStatusViewAlignment(true /* animate */);
         int userIconHeight = mKeyguardQsUserSwitchController != null
                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
         float expandedFraction =
@@ -1332,12 +1364,31 @@
         updateClock();
     }
 
+    private void updateKeyguardStatusViewAlignment(boolean animate) {
+        boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+                .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+        boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications;
+        if (mStatusViewCentered != shouldBeCentered) {
+            mStatusViewCentered = shouldBeCentered;
+            ConstraintSet constraintSet = new ConstraintSet();
+            constraintSet.clone(mNotificationContainerParent);
+            int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline;
+            constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
+            if (animate) {
+                ChangeBounds transition = new ChangeBounds();
+                transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+                transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
+            }
+            constraintSet.applyTo(mNotificationContainerParent);
+        }
+    }
+
     /**
      * @return the padding of the stackscroller when unlocked
      */
     private int getUnlockedStackScrollerPadding() {
-        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
-                + mQsNotificationTopPadding;
+        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
     }
 
     /**
@@ -1360,6 +1411,7 @@
 
         float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
         bottomPadding = Math.max(lockIconPadding, bottomPadding);
+        mKeyguardNotificationBottomPadding = bottomPadding;
 
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight()
@@ -2269,7 +2321,8 @@
 
     private void updateQSExpansionEnabledAmbient() {
         final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
-        mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop;
+        mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
+                || (mAmbientState.getScrollY() <= scrollRangeToTop);
         setQsExpansionEnabled();
     }
 
@@ -2301,8 +2354,8 @@
             left = 0;
             right = getView().getRight() + mDisplayRightInset;
         } else {
-            top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
-            bottom = mNotificationStackScrollLayoutController.getHeight();
+            top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
+            bottom = top + mNotificationStackScrollLayoutController.getHeight();
             left = mNotificationStackScrollLayoutController.getLeft();
             right = mNotificationStackScrollLayoutController.getRight();
         }
@@ -2401,7 +2454,7 @@
         int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
         int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
         int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
-        int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+        int nsslBottom = bottom;
         int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
         mNotificationStackScrollLayoutController.setRoundedClippingBounds(
                 nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
@@ -2442,7 +2495,7 @@
 
     private float calculateNotificationsTopPadding() {
         if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
-            return mSplitShadeNotificationsTopPadding + mQsNotificationTopPadding;
+            return 0;
         }
         if (mKeyguardShowing && (mQsExpandImmediate
                 || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -2453,7 +2506,7 @@
             // panel. We need to take the maximum and linearly interpolate with the panel expansion
             // for a nice motion.
             int maxNotificationPadding = getKeyguardNotificationStaticPadding();
-            int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
+            int maxQsPadding = mQsMaxExpansionHeight;
             int max = mBarState == KEYGUARD ? Math.max(
                     maxNotificationPadding, maxQsPadding) : maxQsPadding;
             return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
@@ -2466,10 +2519,10 @@
             // We can only do the smoother transition on Keyguard when we also are not collapsing
             // from a scrolled quick settings.
             return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
-                    (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
+                    (float) (mQsMaxExpansionHeight),
                     computeQsExpansionFraction());
         } else {
-            return mQsExpansionHeight + mQsNotificationTopPadding;
+            return mQsExpansionHeight;
         }
     }
 
@@ -2855,10 +2908,6 @@
         }
         int maxQsHeight = mQsMaxExpansionHeight;
 
-        if (mKeyguardShowing) {
-            maxQsHeight += mQsNotificationTopPadding;
-        }
-
         // If an animation is changing the size of the QS panel, take the animated value.
         if (mQsSizeChangeAnimator != null) {
             maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
@@ -4301,8 +4350,7 @@
             if (mAccessibilityManager.isEnabled()) {
                 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
             }
-            mNotificationStackScrollLayoutController.setMaxTopPadding(
-                    mQsMaxExpansionHeight + mQsNotificationTopPadding);
+            mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
         }
     }
 
@@ -4424,6 +4472,8 @@
             maybeAnimateBottomAreaAlpha();
             resetHorizontalPanelPosition();
             updateQsState();
+            mSplitShadeHeaderController.setShadeExpanded(
+                    mBarState == SHADE || mBarState == SHADE_LOCKED);
         }
 
         @Override
@@ -4509,8 +4559,7 @@
             if (mQs != null) {
                 updateQSMinHeight();
                 mQsMaxExpansionHeight = mQs.getDesiredHeight();
-                mNotificationStackScrollLayoutController.setMaxTopPadding(
-                        mQsMaxExpansionHeight + mQsNotificationTopPadding);
+                mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
             }
             positionClockAndNotifications();
             if (mQsExpanded && mQsFullyExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5e105bb..1a404dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -474,7 +474,8 @@
         for (StatusBarWindowCallback cb : activeCallbacks) {
             cb.onStateChanged(mCurrentState.mKeyguardShowing,
                     mCurrentState.mKeyguardOccluded,
-                    mCurrentState.mBouncerShowing);
+                    mCurrentState.mBouncerShowing,
+                    mCurrentState.mDozing);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index ed8fb31..68e28cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -22,7 +22,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.WindowInsets;
-import android.widget.FrameLayout;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 
@@ -31,7 +30,6 @@
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -42,8 +40,8 @@
 public class NotificationsQuickSettingsContainer extends ConstraintLayout
         implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
 
-    private FrameLayout mQsFrame;
-    private NotificationStackScrollLayout mStackScroller;
+    private View mQsFrame;
+    private View mStackScroller;
     private View mKeyguardStatusBar;
     private boolean mQsExpanded;
     private boolean mCustomizerAnimating;
@@ -52,10 +50,10 @@
 
     private int mBottomPadding;
     private int mStackScrollerMargin;
-    private boolean mHasViewsAboveShelf;
     private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
     private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
     private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
+    private boolean mSplitShadeEnabled;
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -144,7 +142,6 @@
     public void setCustomizerShowing(boolean isShowing) {
         mCustomizing = isShowing;
         updateBottomMargin();
-        mStackScroller.setQsCustomizerShowing(isShowing);
     }
 
     public void setDetailShowing(boolean isShowing) {
@@ -152,8 +149,19 @@
         updateBottomMargin();
     }
 
+    /**
+     * Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
+     * customizer state
+     */
+    public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+        mSplitShadeEnabled = splitShadeEnabled;
+        // in case device was rotated while showing QS details/customizer
+        updateBottomMargin();
+    }
+
     private void updateBottomMargin() {
-        if (mCustomizing || mDetailShowing) {
+        // in split shade, QS state changes should not influence notifications panel
+        if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
             // Clear out bottom paddings/margins so the qs customization can be full height.
             setPadding(0, 0, 0, 0);
             setBottomMargin(mStackScroller, 0);
@@ -171,7 +179,6 @@
 
     @Override
     public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
-        mHasViewsAboveShelf = hasViewsAboveShelf;
         invalidate();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cfcea96..44ed279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -359,21 +359,7 @@
         mAnimateChange = state.getAnimateChange();
         mAnimationDuration = state.getAnimationDuration();
 
-        mInFrontTint = state.getFrontTint();
-        mBehindTint = state.getBehindTint();
-        mNotificationsTint = state.getNotifTint();
-        mBubbleTint = state.getBubbleTint();
-
-        mInFrontAlpha = state.getFrontAlpha();
-        mBehindAlpha = state.getBehindAlpha();
-        mBubbleAlpha = state.getBubbleAlpha();
-        mNotificationsAlpha = state.getNotifAlpha();
-        if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
-            throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: "
-                    + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
-                    + mNotificationsAlpha);
-        }
-        applyStateToAlpha();
+        applyState();
 
         // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
         // We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -637,7 +623,19 @@
         }
     }
 
-    private void applyStateToAlpha() {
+    private void applyState() {
+        mInFrontTint = mState.getFrontTint();
+        mBehindTint = mState.getBehindTint();
+        mNotificationsTint = mState.getNotifTint();
+        mBubbleTint = mState.getBubbleTint();
+
+        mInFrontAlpha = mState.getFrontAlpha();
+        mBehindAlpha = mState.getBehindAlpha();
+        mBubbleAlpha = mState.getBubbleAlpha();
+        mNotificationsAlpha = mState.getNotifAlpha();
+
+        assertAlphasValid();
+
         if (!mExpansionAffectsAlpha) {
             return;
         }
@@ -695,6 +693,11 @@
                 mBehindTint = behindTint;
             }
         }
+
+        assertAlphasValid();
+    }
+
+    private void assertAlphasValid() {
         if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
             throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
                     + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
@@ -734,7 +737,7 @@
 
 
     private void applyAndDispatchState() {
-        applyStateToAlpha();
+        applyState();
         if (mUpdatePending) {
             return;
         }
@@ -1203,6 +1206,7 @@
         pw.println(" ScrimController: ");
         pw.print("  state: ");
         pw.println(mState);
+        pw.println("    mClipQsScrim = " + mState.mClipQsScrim);
 
         pw.print("  frontScrim:");
         pw.print(" viewAlpha=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index d4458e2..edacbe1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -48,7 +48,7 @@
     protected final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final int mDisplayId;
-    protected final Lazy<StatusBar> mStatusBarLazy;
+    protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final Optional<Bubbles> mBubblesOptional;
 
@@ -61,7 +61,7 @@
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             WindowManager windowManager,
-            Lazy<StatusBar> statusBarLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             Lazy<AssistManager> assistManagerLazy,
             Optional<Bubbles> bubblesOptional
     ) {
@@ -71,7 +71,7 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
         // TODO: Remove circular reference to StatusBar when possible.
-        mStatusBarLazy = statusBarLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mAssistManagerLazy = assistManagerLazy;
         mBubblesOptional = bubblesOptional;
     }
@@ -210,7 +210,7 @@
     }
 
     private StatusBar getStatusBar() {
-        return mStatusBarLazy.get();
+        return mStatusBarOptionalLazy.get().get();
     }
 
     private NotificationPresenter getPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
new file mode 100644
index 0000000..70f3436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.view.View
+import com.android.systemui.BatteryMeterView
+import com.android.systemui.R
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import javax.inject.Inject
+import javax.inject.Named
+
+@StatusBarScope
+class SplitShadeHeaderController @Inject constructor(
+    @Named(SPLIT_SHADE_HEADER) private val statusBar: View,
+    private val statusBarIconController: StatusBarIconController,
+    qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
+    featureFlags: FeatureFlags
+) {
+
+    // TODO(b/194178072) Handle RSSI hiding when multi carrier
+    private val iconManager: StatusBarIconController.IconManager
+    private val qsCarrierGroupController: QSCarrierGroupController
+    private var visible = false
+
+    var shadeExpanded = false
+        set(value) {
+            field = value
+            updateVisibility()
+        }
+
+    var splitShadeMode = false
+        set(value) {
+            field = value
+            updateVisibility()
+        }
+
+    init {
+        val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
+        // battery settings same as in QS icons
+        batteryIcon.setIgnoreTunerUpdates(true)
+        batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+        val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+        iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+        qsCarrierGroupController = qsCarrierGroupControllerBuilder
+                .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
+                .build()
+    }
+
+    private fun updateVisibility() {
+        val shouldBeVisible = shadeExpanded && splitShadeMode
+        if (visible != shouldBeVisible) {
+            visible = shouldBeVisible
+            statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+            updateListeners(shouldBeVisible)
+        }
+    }
+
+    private fun updateListeners(visible: Boolean) {
+        qsCarrierGroupController.setListening(visible)
+        if (visible) {
+            statusBarIconController.addIconGroup(iconManager)
+        } else {
+            statusBarIconController.removeIconGroup(iconManager)
+        }
+    }
+}
\ No newline at end of file
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 972869c..3870b40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -105,6 +105,7 @@
 import android.view.Display;
 import android.view.IRemoteAnimationRunner;
 import android.view.IWindowManager;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -186,6 +187,7 @@
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CircleReveal;
@@ -244,6 +246,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -467,6 +470,8 @@
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
     private final FeatureFlags mFeatureFlags;
+    private final UnfoldTransitionConfig mUnfoldTransitionConfig;
+    private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
@@ -801,6 +806,8 @@
             NotificationIconAreaController notificationIconAreaController,
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
+            UnfoldTransitionConfig unfoldTransitionConfig,
+            Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
@@ -888,6 +895,8 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
         mChargingRippleAnimationController = chargingRippleAnimationController;
+        mUnfoldTransitionConfig = unfoldTransitionConfig;
+        mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
         mStatusBarLocationPublisher = locationPublisher;
@@ -991,7 +1000,8 @@
             showTransientUnchecked();
         }
         onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
-                result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen);
+                result.mNavbarColorManagedByIme, result.mBehavior, result.mRequestedState,
+                result.mPackageName);
 
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
@@ -1059,6 +1069,10 @@
 
         mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
 
+        if (mUnfoldTransitionConfig.isEnabled()) {
+            mUnfoldLightRevealOverlayAnimation.get().init();
+        }
+
         mPluginManager.addPluginListener(
                 new PluginListener<OverlayPlugin>() {
                     private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
@@ -1198,7 +1212,7 @@
                                 mKeyguardStateController,
                                 mNetworkController,
                                 mStatusBarStateController,
-                                this,
+                                () -> Optional.of(this),
                                 mCommandQueue
                         ),
                         CollapsedStatusBarFragment.TAG)
@@ -1461,7 +1475,7 @@
                         .setNotificationPresenter(mPresenter)
                         .setNotificationPanelViewController(mNotificationPanelViewController)
                         .build();
-        mStackScroller.setNotificationActivityStarter(mNotificationActivityStarter);
+        mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
 
         mNotificationsController.initialize(
@@ -2501,7 +2515,7 @@
     @Override
     public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, boolean isFullscreen) {
+            @Behavior int behavior, InsetsState requestedState, String packageName) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -2514,7 +2528,8 @@
                 mStatusBarMode, navbarColorManagedByIme);
 
         updateBubblesVisibility();
-        mStatusBarStateController.setFullscreenState(isFullscreen);
+        mStatusBarStateController.setSystemBarAttributes(
+                appearance, behavior, requestedState, packageName);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index edcf261..1dd22b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -155,13 +155,30 @@
         val dc = context.display.cutout
         val currentRotation = RotationUtils.getExactRotation(context)
 
+        val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+        val roundedCornerPadding = rotatedResources
+                .getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
+        val minDotWidth = rotatedResources
+                .getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
+
+        val minLeft: Int
+        val minRight: Int
+        if (isRtl) {
+            minLeft = max(minDotWidth, roundedCornerPadding)
+            minRight = roundedCornerPadding
+        } else {
+            minLeft = roundedCornerPadding
+            minRight = max(minDotWidth, roundedCornerPadding)
+        }
+
         return calculateInsetsForRotationWithRotatedResources(
                 currentRotation,
                 targetRotation,
                 dc,
                 windowManager.maximumWindowMetrics,
                 rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
-                rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding))
+                minLeft,
+                minRight)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -212,9 +229,12 @@
  * Calculates the exact left and right positions for the status bar contents for the given
  * rotation
  *
- * @param rot rotation for which to query the margins
- * @param context systemui context
- * @param rotatedResources resources constructed with the proper orientation set
+ * @param currentRotation current device rotation
+ * @param targetRotation rotation for which to calculate the status bar content rect
+ * @param displayCutout [DisplayCutout] for the curren display. possibly null
+ * @param windowMetrics [WindowMetrics] for the current window
+ * @param statusBarHeight height of the status bar for the target rotation
+ * @param roundedCornerPadding from rounded_corner_content_padding
  *
  * @see [RotationUtils#getResourcesForRotation]
  */
@@ -224,7 +244,8 @@
     displayCutout: DisplayCutout?,
     windowMetrics: WindowMetrics,
     statusBarHeight: Int,
-    roundedCornerPadding: Int
+    minLeft: Int,
+    minRight: Int
 ): Rect {
     /*
     TODO: Check if this is ever used for devices with no rounded corners
@@ -242,7 +263,8 @@
             rotZeroBounds.bottom,
             currentBounds.width(),
             currentBounds.height(),
-            roundedCornerPadding,
+            minLeft,
+            minRight,
             targetRotation,
             currentRotation)
 
@@ -256,7 +278,10 @@
  * @param sbHeight appropriate status bar height for this rotation
  * @param width display width calculated for ROTATION_NONE
  * @param height display height calculated for ROTATION_NONE
- * @param roundedCornerPadding rounded_corner_content_padding dimension
+ * @param cWidth display width in our current rotation
+ * @param cHeight display height in our current rotation
+ * @param minLeft the minimum padding to enforce on the left
+ * @param minRight the minimum padding to enforce on the right
  * @param targetRotation the rotation for which to calculate margins
  * @param currentRotation the rotation from which the display cutout was generated
  *
@@ -270,7 +295,8 @@
     height: Int,
     cWidth: Int,
     cHeight: Int,
-    roundedCornerPadding: Int,
+    minLeft: Int,
+    minRight: Int,
     @Rotation targetRotation: Int,
     @Rotation currentRotation: Int
 ): Rect {
@@ -279,9 +305,9 @@
 
     val cutoutRects = dc?.boundingRects
     if (cutoutRects == null || cutoutRects.isEmpty()) {
-        return Rect(roundedCornerPadding,
+        return Rect(minLeft,
                 0,
-                logicalDisplayWidth - roundedCornerPadding,
+                logicalDisplayWidth - minRight,
                 sbHeight)
     }
 
@@ -294,8 +320,8 @@
     // Size of the status bar window for the given rotation relative to our exact rotation
     val sbRect = sbRect(relativeRotation, sbHeight, Pair(cWidth, cHeight))
 
-    var leftMargin = roundedCornerPadding
-    var rightMargin = roundedCornerPadding
+    var leftMargin = minLeft
+    var rightMargin = minRight
     for (cutoutRect in cutoutRects) {
         // There is at most one non-functional area per short edge of the device. So if the status
         // bar doesn't share a short edge with the cutout, we can ignore its insets because there
@@ -306,11 +332,11 @@
 
         if (cutoutRect.touchesLeftEdge(relativeRotation, cWidth, cHeight)) {
 
-            val l = max(roundedCornerPadding, cutoutRect.logicalWidth(relativeRotation))
+            val l = max(minLeft, cutoutRect.logicalWidth(relativeRotation))
             leftMargin = max(l, leftMargin)
         } else if (cutoutRect.touchesRightEdge(relativeRotation, cWidth, cHeight)) {
             val logicalWidth = cutoutRect.logicalWidth(relativeRotation)
-            rightMargin = max(roundedCornerPadding, logicalWidth)
+            rightMargin = max(minRight, logicalWidth)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8a7708a..95fd886d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -970,6 +970,9 @@
             mStatusBar.setBouncerShowing(bouncerShowing);
         }
 
+        if (occluded != mLastOccluded || mFirstUpdate) {
+            mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded);
+        }
         if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
             mKeyguardUpdateManager.onKeyguardVisibilityChanged(showing && !occluded);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index f33ff27..ac43b67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -16,5 +16,6 @@
 package com.android.systemui.statusbar.phone;
 
 public interface StatusBarWindowCallback {
-    void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing);
+    void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
+            boolean isDozing);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 9a25a70..3d3b58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -34,6 +36,7 @@
 import android.util.Log;
 import android.view.Gravity;
 import android.view.IWindowManager;
+import android.view.Surface;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
@@ -118,21 +121,7 @@
         // Now that the status bar window encompasses the sliding panel and its
         // translucent backdrop, the entire thing is made TRANSLUCENT and is
         // hardware-accelerated.
-        mLp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                mBarHeight,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.TRANSLUCENT);
-        mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
-        mLp.token = new Binder();
-        mLp.gravity = Gravity.TOP;
-        mLp.setFitInsetsTypes(0 /* types */);
-        mLp.setTitle("StatusBar");
-        mLp.packageName = mContext.getPackageName();
-        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
 
         mWindowManager.addView(mStatusBarView, mLp);
         mLpChanged.copyFrom(mLp);
@@ -141,6 +130,63 @@
         calculateStatusBarLocationsForAllRotations();
     }
 
+    private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+        WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+        lp.paramsForRotation = new WindowManager.LayoutParams[4];
+        for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+            lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+        }
+        return lp;
+    }
+
+    private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+        int height = mBarHeight;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+            int defaultAndUpsideDownHeight;
+            int theOtherHeight;
+            if (displayBounds.width() > displayBounds.height()) {
+                defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height_landscape);
+                theOtherHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height_portrait);
+            } else {
+                defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height_portrait);
+                theOtherHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height_landscape);
+            }
+            switch (rotation) {
+                case ROTATION_UNDEFINED:
+                case Surface.ROTATION_0:
+                case Surface.ROTATION_180:
+                    height = defaultAndUpsideDownHeight;
+                    break;
+                case Surface.ROTATION_90:
+                case Surface.ROTATION_270:
+                    height = theOtherHeight;
+                    break;
+            }
+        }
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.MATCH_PARENT,
+                height,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+                PixelFormat.TRANSLUCENT);
+        lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+        lp.token = new Binder();
+        lp.gravity = Gravity.TOP;
+        lp.setFitInsetsTypes(0 /* types */);
+        lp.setTitle("StatusBar");
+        lp.packageName = mContext.getPackageName();
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        return lp;
+
+    }
+
     private void calculateStatusBarLocationsForAllRotations() {
         Rect[] bounds = new Rect[4];
         bounds[0] = mContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index fb25ae3..4fab226 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.lang.annotation.Documented;
@@ -86,4 +87,10 @@
      */
     @StatusBarScope
     AuthRippleController getAuthRippleController();
+
+    /**
+     * Creates a SplitShadeHeaderController.
+     */
+    @StatusBarScope
+    SplitShadeHeaderController getSplitShadeHeaderController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b6e8bd8..5e8eecb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -48,6 +48,7 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -106,6 +107,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -214,6 +216,8 @@
             NotificationIconAreaController notificationIconAreaController,
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
+            UnfoldTransitionConfig unfoldTransitionConfig,
+            Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
@@ -304,6 +308,8 @@
                 notificationIconAreaController,
                 brightnessSliderFactory,
                 chargingRippleAnimationController,
+                unfoldTransitionConfig,
+                unfoldLightRevealOverlayAnimation,
                 ongoingCallController,
                 animationScheduler,
                 locationPublisher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 27d71ed..d691dca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone.dagger;
 
 import android.annotation.Nullable;
+import android.view.View;
 
 import com.android.keyguard.LockIconView;
 import com.android.systemui.R;
@@ -25,11 +26,16 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.TapAgainView;
 
+import javax.inject.Named;
+
 import dagger.Module;
 import dagger.Provides;
 
 @Module
 public abstract class StatusBarViewModule {
+
+    public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope
@@ -57,6 +63,15 @@
 
     /** */
     @Provides
+    @Named(SPLIT_SHADE_HEADER)
+    @StatusBarComponent.StatusBarScope
+    public static View getSlitShadeStatusBarView(
+            NotificationShadeWindowView notificationShadeWindowView) {
+        return notificationShadeWindowView.findViewById(R.id.split_shade_status_bar);
+    }
+
+    /** */
+    @Provides
     @StatusBarComponent.StatusBarScope
     public static TapAgainView getTapAgainView(NotificationPanelView npv) {
         return npv.getTapAgainView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index ab58286..4ca1f60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiPickerTracker;
 
@@ -118,6 +119,11 @@
                 new UserHandle(mCurrentUser));
     }
 
+    public boolean canConfigMobileData() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+                UserHandle.of(mCurrentUser));
+    }
+
     public void onUserSwitched(int newUserId) {
         mCurrentUser = newUserId;
     }
@@ -157,6 +163,15 @@
     }
 
     @Override
+    public MergedCarrierEntry getMergedCarrierEntry() {
+        if (mWifiPickerTracker == null) {
+            fireAcccessPointsCallback(Collections.emptyList());
+            return null;
+        }
+        return mWifiPickerTracker.getMergedCarrierEntry();
+    }
+
+    @Override
     public int getIcon(WifiEntry ap) {
         int level = ap.getLevel();
         return ICONS[Math.max(0, level)];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index fcfc967..742b1ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -245,5 +245,11 @@
          * animation.
          */
         default void onKeyguardDismissAmountChanged() {}
+
+        /**
+         * Triggered when face auth becomes available or unavailable. Value should be queried with
+         * {@link KeyguardStateController#isFaceAuthEnabled()}.
+         */
+        default void onFaceAuthEnabledChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index ef2ca98..eeea699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -23,6 +23,7 @@
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
 import java.util.List;
@@ -223,9 +224,11 @@
         void addAccessPointCallback(AccessPointCallback callback);
         void removeAccessPointCallback(AccessPointCallback callback);
         void scanForAccessPoints();
+        MergedCarrierEntry getMergedCarrierEntry();
         int getIcon(WifiEntry ap);
         boolean connect(WifiEntry ap);
         boolean canConfigWifi();
+        boolean canConfigMobileData();
 
         public interface AccessPointCallback {
             void onAccessPointsChanged(List<WifiEntry> accessPoints);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fa61115..52c37a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -68,8 +68,11 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -189,6 +192,8 @@
     private boolean mUserSetup;
     private boolean mSimDetected;
     private boolean mForceCellularValidated;
+    private InternetDialogFactory mInternetDialogFactory;
+    private Handler mMainHandler;
 
     private ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -218,6 +223,8 @@
             AccessPointControllerImpl accessPointController,
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
+            @Main Handler handler,
+            InternetDialogFactory internetDialogFactory,
             FeatureFlags featureFlags) {
         this(context, connectivityManager,
                 telephonyManager,
@@ -238,6 +245,8 @@
                 carrierConfigTracker,
                 featureFlags);
         mReceiverHandler.post(mRegisterListeners);
+        mMainHandler = handler;
+        mInternetDialogFactory = internetDialogFactory;
     }
 
     @VisibleForTesting
@@ -472,6 +481,9 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        if (InternetDialogUtil.isProviderModelEnabled(mContext)) {
+            filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+        }
         mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
         mListening = true;
 
@@ -780,6 +792,10 @@
                 mConfig = Config.readConfig(mContext);
                 mReceiverHandler.post(this::handleConfigurationChanged);
                 break;
+            case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
+                boolean canConfigMobileData = mAccessPoints.canConfigMobileData();
+                mMainHandler.post(() -> mInternetDialogFactory.create(true, canConfigMobileData));
+                break;
             default:
                 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
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 84d7c05..7f3152c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -72,11 +72,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -151,7 +150,9 @@
         @UiEvent(doc = "User sent data through the notification remote input view")
         NOTIFICATION_REMOTE_INPUT_SEND(797),
         @UiEvent(doc = "Failed attempt to send data through the notification remote input view")
-        NOTIFICATION_REMOTE_INPUT_FAILURE(798);
+        NOTIFICATION_REMOTE_INPUT_FAILURE(798),
+        @UiEvent(doc = "User attached an image to the remote input view")
+        NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE(825);
 
         private final int mId;
         NotificationRemoteInputEvent(int id) {
@@ -283,7 +284,8 @@
         });
     }
 
-    private void setAttachment(ContentInfo item) {
+    @VisibleForTesting
+    protected void setAttachment(ContentInfo item) {
         if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) {
             // We need to release permissions when sending the attachment to the target
             // app or if it is deleted by the user. When sending to the target app, we
@@ -309,6 +311,10 @@
             attachment.setVisibility(GONE);
         } else {
             attachment.setVisibility(VISIBLE);
+            mUiEventLogger.logWithInstanceId(
+                    NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE,
+                    mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
+                    mEntry.getSbn().getInstanceId());
         }
         updateSendButton();
     }
@@ -413,8 +419,6 @@
                 mEntry.getSbn().getPackageName(),
                 mEntry.getSbn().getUser().getIdentifier());
 
-        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
-                mEntry.getSbn().getPackageName());
         mUiEventLogger.logWithInstanceId(
                 NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
                 mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -423,8 +427,6 @@
             mPendingIntent.send(mContext, 0, intent);
         } catch (PendingIntent.CanceledException e) {
             Log.i(TAG, "Unable to send remote input result", e);
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
-                    mEntry.getSbn().getPackageName());
             mUiEventLogger.logWithInstanceId(
                     NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
                     mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -501,8 +503,6 @@
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
 
         if (logClose) {
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
-                    mEntry.getSbn().getPackageName());
             mUiEventLogger.logWithInstanceId(
                     NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_CLOSE,
                     mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -584,8 +584,6 @@
     }
 
     public void focus() {
-        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
-                mEntry.getSbn().getPackageName());
         mUiEventLogger.logWithInstanceId(
                 NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN,
                 mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
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 c94eaed..6af632d 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.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.IActivityTaskManager;
 import android.content.BroadcastReceiver;
@@ -71,9 +72,11 @@
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.qs.QSUserSwitcherEvent;
 import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.CreateUserActivity;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -104,9 +107,12 @@
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     protected final Context mContext;
+    protected final UserTracker mUserTracker;
     protected final UserManager mUserManager;
+    private final ContentObserver mSettingsObserver;
     private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
-    private final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
+    @VisibleForTesting
+    final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
     private final KeyguardStateController mKeyguardStateController;
     protected final Handler mHandler;
     private final ActivityStarter mActivityStarter;
@@ -115,15 +121,18 @@
     private final IActivityTaskManager mActivityTaskManager;
 
     private ArrayList<UserRecord> mUsers = new ArrayList<>();
-    private Dialog mExitGuestDialog;
-    private Dialog mAddUserDialog;
+    @VisibleForTesting
+    AlertDialog mExitGuestDialog;
+    @VisibleForTesting
+    Dialog mAddUserDialog;
     private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
     private boolean mResumeUserOnGuestLogout = true;
     private boolean mSimpleUserSwitcher;
     // When false, there won't be any visual affordance to add a new user from the keyguard even if
     // the user is unlocked
     private boolean mAddUsersFromLockScreen;
-    private boolean mPauseRefreshUsers;
+    @VisibleForTesting
+    boolean mPauseRefreshUsers;
     private int mSecondaryUser = UserHandle.USER_NULL;
     private Intent mSecondaryUserServiceIntent;
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
@@ -136,6 +145,8 @@
 
     @Inject
     public UserSwitcherController(Context context,
+            UserManager userManager,
+            UserTracker userTracker,
             KeyguardStateController keyguardStateController,
             @Main Handler handler,
             ActivityStarter activityStarter,
@@ -145,14 +156,17 @@
             TelephonyListenerManager telephonyListenerManager,
             IActivityTaskManager activityTaskManager,
             UserDetailAdapter userDetailAdapter,
+            SecureSettings secureSettings,
             @UiBackground Executor uiBgExecutor) {
         mContext = context;
+        mUserTracker = userTracker;
         mBroadcastDispatcher = broadcastDispatcher;
         mTelephonyListenerManager = telephonyListenerManager;
         mActivityTaskManager = activityTaskManager;
         mUiEventLogger = uiEventLogger;
         mFalsingManager = falsingManager;
-        mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(this, mUiEventLogger);
+        mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
+                this, mUserTracker, mUiEventLogger, secureSettings);
         mUserDetailAdapter = userDetailAdapter;
         mUiBgExecutor = uiBgExecutor;
         if (!UserManager.isGuestUserEphemeral()) {
@@ -164,7 +178,7 @@
         mKeyguardStateController = keyguardStateController;
         mHandler = handler;
         mActivityStarter = activityStarter;
-        mUserManager = UserManager.get(context);
+        mUserManager = userManager;
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -183,6 +197,14 @@
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
                 PERMISSION_SELF, null /* scheduler */);
 
+        mSettingsObserver = new ContentObserver(mHandler) {
+            public void onChange(boolean selfChange) {
+                mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
+                mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
+                refreshUsers(UserHandle.USER_NULL);
+            };
+        };
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
                 mSettingsObserver);
@@ -244,11 +266,11 @@
                     return null;
                 }
                 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
-                int currentId = ActivityManager.getCurrentUser();
+                int currentId = mUserTracker.getUserId();
                 // Check user switchability of the foreground user since SystemUI is running in
                 // User 0
                 boolean canSwitchUsers = mUserManager.getUserSwitchability(
-                        UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK;
+                        UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
 
@@ -376,7 +398,7 @@
     }
 
     public void logoutCurrentUser() {
-        int currentUser = ActivityManager.getCurrentUser();
+        int currentUser = mUserTracker.getUserId();
         if (currentUser != UserHandle.USER_SYSTEM) {
             pauseRefreshUsers();
             ActivityManager.logoutCurrentUser();
@@ -388,7 +410,7 @@
             Log.w(TAG, "User " + userId + " could not removed.");
             return;
         }
-        if (ActivityManager.getCurrentUser() == userId) {
+        if (mUserTracker.getUserId() == userId) {
             switchToUserId(UserHandle.USER_SYSTEM);
         }
         if (mUserManager.removeUser(userId)) {
@@ -396,7 +418,8 @@
         }
     }
 
-    private void onUserListItemClicked(UserRecord record) {
+    @VisibleForTesting
+    void onUserListItemClicked(UserRecord record) {
         int id;
         if (record.isGuest && record.info == null) {
             // No guest user. Create one.
@@ -414,7 +437,7 @@
             id = record.info.id;
         }
 
-        int currUserId = ActivityManager.getCurrentUser();
+        int currUserId = mUserTracker.getUserId();
         if (currUserId == id) {
             if (record.isGuest) {
                 showExitGuestDialog(id);
@@ -572,15 +595,6 @@
         }
     };
 
-    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
-        public void onChange(boolean selfChange) {
-            mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
-            mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
-            refreshUsers(UserHandle.USER_NULL);
-        };
-    };
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("UserSwitcherController state:");
@@ -639,13 +653,7 @@
      * UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
      */
     public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
-        UserInfo currentUser;
-        try {
-            currentUser = ActivityManager.getService().getCurrentUser();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't remove guest because ActivityManager is dead");
-            return;
-        }
+        UserInfo currentUser = mUserTracker.getUserInfo();
         if (currentUser.id != guestUserId) {
             Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
                     + " is not current user (" + currentUser.id + ")");
@@ -839,9 +847,9 @@
 
     private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
         EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
-                UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
+                UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
         if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
-                UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
+                UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId())) {
             record.isDisabledByAdmin = true;
             record.enforcedAdmin = admin;
         } else {
@@ -1053,7 +1061,8 @@
         }
     }
 
-    private final class AddUserDialog extends SystemUIDialog implements
+    @VisibleForTesting
+    final class AddUserDialog extends SystemUIDialog implements
             DialogInterface.OnClickListener {
 
         public AddUserDialog(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
new file mode 100644
index 0000000..ae9d9ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.Context
+import android.text.StaticLayout
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+
+/**
+ * View for showing a date that can toggle between two different formats depending on size.
+ *
+ * If no pattern can fit, it will display empty.
+ *
+ * @see R.styleable.VariableDateView_longDatePattern
+ * @see R.styleable.VariableDateView_shortDatePattern
+ */
+class VariableDateView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {
+
+    val longerPattern: String
+    val shorterPattern: String
+
+    init {
+        val a = context.theme.obtainStyledAttributes(
+                attrs,
+                R.styleable.VariableDateView,
+                0, 0)
+        longerPattern = a.getString(R.styleable.VariableDateView_longDatePattern)
+                ?: context.getString(R.string.system_ui_date_pattern)
+        shorterPattern = a.getString(R.styleable.VariableDateView_shortDatePattern)
+                ?: context.getString(R.string.abbrev_month_day_no_year)
+
+        a.recycle()
+    }
+
+    /**
+     * Freeze the pattern switching
+     *
+     * Use during animations if the container will change its size but this view should not change
+     */
+    var freezeSwitching = false
+
+    private var onMeasureListener: OnMeasureListener? = null
+
+    fun onAttach(listener: OnMeasureListener?) {
+        onMeasureListener = listener
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        val availableWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd
+        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED && !freezeSwitching) {
+            onMeasureListener?.onMeasureAction(availableWidth)
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+    }
+
+    fun getDesiredWidthForText(text: CharSequence): Float {
+        return StaticLayout.getDesiredWidth(text, paint)
+    }
+
+    interface OnMeasureListener {
+        fun onMeasureAction(availableWidth: Int)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
new file mode 100644
index 0000000..99d84c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.icu.text.DateFormat
+import android.icu.text.DisplayContext
+import android.icu.util.Calendar
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import android.text.TextUtils
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dependency
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
+import java.text.FieldPosition
+import java.text.ParsePosition
+import java.util.Date
+import java.util.Locale
+import javax.inject.Inject
+import javax.inject.Named
+
+@VisibleForTesting
+internal fun getTextForFormat(date: Date?, format: DateFormat): String {
+    return if (format === EMPTY_FORMAT) { // Check if same object
+        ""
+    } else format.format(date)
+}
+
+@VisibleForTesting
+internal fun getFormatFromPattern(pattern: String?): DateFormat {
+    if (TextUtils.equals(pattern, "")) {
+        return EMPTY_FORMAT
+    }
+    val l = Locale.getDefault()
+    val format = DateFormat.getInstanceForSkeleton(pattern, l)
+    format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
+    return format
+}
+
+private val EMPTY_FORMAT: DateFormat = object : DateFormat() {
+    override fun format(
+        cal: Calendar,
+        toAppendTo: StringBuffer,
+        fieldPosition: FieldPosition
+    ): StringBuffer? {
+        return null
+    }
+
+    override fun parse(text: String, cal: Calendar, pos: ParsePosition) {}
+}
+
+private const val DEBUG = false
+private const val TAG = "VariableDateViewController"
+
+class VariableDateViewController(
+    private val systemClock: SystemClock,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val timeTickHandler: Handler,
+    view: VariableDateView
+) : ViewController<VariableDateView>(view) {
+
+    private var dateFormat: DateFormat? = null
+    private var datePattern = view.longerPattern
+        set(value) {
+            if (field == value) return
+            field = value
+            dateFormat = null
+            if (isAttachedToWindow) {
+                post(::updateClock)
+            }
+        }
+    private var lastWidth = Integer.MAX_VALUE
+    private var lastText = ""
+    private var currentTime = Date()
+
+    // View class easy accessors
+    private val longerPattern: String
+        get() = mView.longerPattern
+    private val shorterPattern: String
+        get() = mView.shorterPattern
+    private fun post(block: () -> Unit) = mView.handler?.post(block)
+
+    private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            // If the handler is null, it means we received a broadcast while the view has not
+            // finished being attached or in the process of being detached.
+            // In that case, do not post anything.
+            val handler = mView.handler ?: return
+            val action = intent.action
+            if (
+                    Intent.ACTION_TIME_TICK == action ||
+                    Intent.ACTION_TIME_CHANGED == action ||
+                    Intent.ACTION_TIMEZONE_CHANGED == action ||
+                    Intent.ACTION_LOCALE_CHANGED == action
+            ) {
+                if (
+                        Intent.ACTION_LOCALE_CHANGED == action ||
+                        Intent.ACTION_TIMEZONE_CHANGED == action
+                ) {
+                    // need to get a fresh date format
+                    handler.post { dateFormat = null }
+                }
+                handler.post(::updateClock)
+            }
+        }
+    }
+
+    private val onMeasureListener = object : VariableDateView.OnMeasureListener {
+        override fun onMeasureAction(availableWidth: Int) {
+            if (availableWidth != lastWidth) {
+                // maybeChangeFormat will post if the pattern needs to change.
+                maybeChangeFormat(availableWidth)
+                lastWidth = availableWidth
+            }
+        }
+    }
+
+    override fun onViewAttached() {
+        val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_TIME_TICK)
+            addAction(Intent.ACTION_TIME_CHANGED)
+            addAction(Intent.ACTION_TIMEZONE_CHANGED)
+            addAction(Intent.ACTION_LOCALE_CHANGED)
+        }
+
+        broadcastDispatcher.registerReceiver(intentReceiver, filter,
+                HandlerExecutor(timeTickHandler), UserHandle.SYSTEM)
+
+        post(::updateClock)
+        mView.onAttach(onMeasureListener)
+    }
+
+    override fun onViewDetached() {
+        dateFormat = null
+        mView.onAttach(null)
+        broadcastDispatcher.unregisterReceiver(intentReceiver)
+    }
+
+    private fun updateClock() {
+        if (dateFormat == null) {
+            dateFormat = getFormatFromPattern(datePattern)
+        }
+
+        currentTime.time = systemClock.currentTimeMillis()
+
+        val text = getTextForFormat(currentTime, dateFormat!!)
+        if (text != lastText) {
+            mView.setText(text)
+            lastText = text
+        }
+    }
+
+    private fun maybeChangeFormat(availableWidth: Int) {
+        if (mView.freezeSwitching ||
+                availableWidth > lastWidth && datePattern == longerPattern ||
+                availableWidth < lastWidth && datePattern == ""
+        ) {
+            // Nothing to do
+            return
+        }
+        if (DEBUG) Log.d(TAG, "Width changed. Maybe changing pattern")
+        // Start with longer pattern and see what fits
+        var text = getTextForFormat(currentTime, getFormatFromPattern(longerPattern))
+        var length = mView.getDesiredWidthForText(text)
+        if (length <= availableWidth) {
+            changePattern(longerPattern)
+            return
+        }
+
+        text = getTextForFormat(currentTime, getFormatFromPattern(shorterPattern))
+        length = mView.getDesiredWidthForText(text)
+        if (length <= availableWidth) {
+            changePattern(shorterPattern)
+            return
+        }
+
+        changePattern("")
+    }
+
+    private fun changePattern(newPattern: String) {
+        if (newPattern.equals(datePattern)) return
+        if (DEBUG) Log.d(TAG, "Changing pattern to $newPattern")
+        datePattern = newPattern
+    }
+
+    class Factory @Inject constructor(
+        private val systemClock: SystemClock,
+        private val broadcastDispatcher: BroadcastDispatcher,
+        @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
+    ) {
+        fun create(view: VariableDateView): VariableDateViewController {
+            return VariableDateViewController(
+                    systemClock,
+                    broadcastDispatcher,
+                    handler,
+                    view
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 81999b5..9355244 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -325,6 +325,11 @@
 
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
 
+        // All wallpaper color and keyguard logic only applies when Monet is enabled.
+        if (!mIsMonetEnabled) {
+            return;
+        }
+
         // Upon boot, make sure we have the most up to date colors
         Runnable updateColors = () -> {
             WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
new file mode 100644
index 0000000..8e7c49a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.view.Surface
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.unfold.UnfoldTransitionProgressProvider
+import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.LinearLightRevealEffect
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import javax.inject.Inject
+
+@SysUISingleton
+class UnfoldLightRevealOverlayAnimation @Inject constructor(
+    private val context: Context,
+    private val deviceStateManager: DeviceStateManager,
+    private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+    @Main private val executor: Executor,
+    private val windowManager: WindowManager
+) {
+
+    private val transitionListener = TransitionListener()
+    private var scrimView: LightRevealScrim? = null
+
+    fun init() {
+        deviceStateManager.registerCallback(executor, FoldListener())
+        unfoldTransitionProgressProvider.addCallback(transitionListener)
+    }
+
+    private inner class TransitionListener : TransitionProgressListener {
+
+        override fun onTransitionProgress(progress: Float) {
+            scrimView?.revealAmount = progress
+        }
+
+        override fun onTransitionFinished() {
+            removeOverlayView()
+        }
+
+        override fun onTransitionStarted() {
+        }
+    }
+
+    private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
+        if (isFolded) {
+            removeOverlayView()
+        } else {
+            // Add overlay view before starting the transition as soon as we unfolded the device
+            addOverlayView()
+        }
+    })
+
+    private fun addOverlayView() {
+        val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
+        params.height = WindowManager.LayoutParams.MATCH_PARENT
+        params.width = WindowManager.LayoutParams.MATCH_PARENT
+        params.format = PixelFormat.TRANSLUCENT
+
+        // TODO(b/193801466): create a separate type for this overlay
+        params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+        params.title = "Unfold Light Reveal Animation"
+        params.layoutInDisplayCutoutMode =
+            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        params.fitInsetsTypes = 0
+        params.flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+            or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+        params.setTrustedOverlay()
+
+        val rotation = windowManager.defaultDisplay.rotation
+        val isVerticalFold = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+
+        val newScrimView = LightRevealScrim(context, null)
+            .apply {
+                revealEffect = LinearLightRevealEffect(isVerticalFold)
+                revealAmountListener = Consumer {}
+                revealAmount = 0f
+            }
+
+        val packageName: String = newScrimView.context.opPackageName
+        params.packageName = packageName
+        params.hideTimeoutMilliseconds = OVERLAY_HIDE_TIMEOUT_MILLIS
+
+        if (scrimView?.parent != null) {
+            windowManager.removeView(scrimView)
+        }
+
+        this.scrimView = newScrimView
+
+        try {
+            windowManager.addView(scrimView, params)
+        } catch (e: WindowManager.BadTokenException) {
+            e.printStackTrace()
+        }
+    }
+
+    private fun removeOverlayView() {
+        scrimView?.let {
+            if (it.parent != null) {
+                windowManager.removeViewImmediate(it)
+            }
+            scrimView = null
+        }
+    }
+}
+
+private const val OVERLAY_HIDE_TIMEOUT_MILLIS = 10_000L
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index bf00667..a1cdfd8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -29,7 +29,6 @@
 import com.android.systemui.R;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 
 import java.util.List;
 import java.util.function.Consumer;
@@ -161,13 +160,11 @@
     }
 
     /**
-     * Returns true if the device should use the split notification shade, based on feature flags,
-     * orientation and screen width.
+     * Returns true if the device should use the split notification shade, based on orientation and
+     * screen width.
      */
-    public static boolean shouldUseSplitNotificationShade(FeatureFlags featureFlags,
-            Resources resources) {
-        return featureFlags.isTwoColumnNotificationShadeEnabled()
-                && resources.getBoolean(R.bool.config_use_split_notification_shade);
+    public static boolean shouldUseSplitNotificationShade(Resources resources) {
+        return resources.getBoolean(R.bool.config_use_split_notification_shade);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
new file mode 100644
index 0000000..542cf65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.concurrency;
+
+/**
+ * Allows triggering methods based on a passed in id or message, generally on another thread.
+ *
+ * Messages sent on to this router must be processed in order. That is to say, if three
+ * messages are sent with no delay, they must be processed in the order they were sent. Moreover,
+ * if messages are sent with various delays, they must be processed in order of their delay.
+ *
+ *  Messages can be passed by either a simple integer or an instance of a class. Unique integers are
+ *  considered unique messages. Unique message classes (not instances) are considered unique
+ *  messages. You can use message classes to pass extra data for processing to subscribers.
+ *
+ *  <pre>
+ *      // Three messages with three unique integer messages.
+ *      // They can be subscribed to independently.
+ *      router.sendMessage(0);
+ *      router.sendMessage(1);
+ *      router.sendMessage(2);
+ *
+ *      // Three messages with two unique message classes.
+ *      // The first and third messages will be delivered to the same subscribers.
+ *      router.sendMessage(new Foo(0));
+ *      router.sendMessage(new Bar(1));
+ *      router.sendMessage(new Foo(2));
+ *  </pre>
+ *
+ * The number of unique ids and message types used should be relatively constrained. Construct
+ * a custom message-class and put unique, per-message data inside of it.
+ */
+public interface MessageRouter {
+    /**
+     * Alerts any listeners subscribed to the passed in id.
+     *
+     * The number of unique ids used should be relatively constrained - used to identify the type
+     * of message being sent. If unique information needs to be passed with each call, use
+     * {@link #sendMessage(Object)}.
+     *
+     * @param id An identifier for the message
+     */
+    default void sendMessage(int id) {
+        sendMessageDelayed(id, 0);
+    }
+
+    /**
+     * Alerts any listeners subscribed to the passed in message.
+     *
+     * The number of message types used should be relatively constrained. If no unique information
+     * needs to be passed in, you can simply use {@link #sendMessage(int)}} which takes an integer
+     * instead of a unique class type.
+     *
+     * The class of the passed in object will be used to router the message.
+     *
+     * @param data A message containing extra data for processing.
+     */
+    default void sendMessage(Object data) {
+        sendMessageDelayed(data, 0);
+    }
+
+    /**
+     * Alerts any listeners subscribed to the passed in id in the future.
+     *
+     * The number of unique ids used should be relatively constrained - used to identify the type
+     * of message being sent. If unique information needs to be passed with each call, use
+     * {@link #sendMessageDelayed(Object, long)}.
+     *
+     * @param id An identifier for the message
+     * @param delayMs Number of milliseconds to wait before alerting.
+     */
+    void sendMessageDelayed(int id, long delayMs);
+
+
+    /**
+     * Alerts any listeners subscribed to the passed in message in the future.
+     *
+     * The number of message types used should be relatively constrained. If no unique information
+     * needs to be passed in, you can simply use {@link #sendMessageDelayed(int, long)} which takes
+     * an integer instead of a unique class type.
+     *
+     * @param data A message containing extra data for processing.
+     * @param delayMs Number of milliseconds to wait before alerting.
+     */
+    void sendMessageDelayed(Object data, long delayMs);
+
+    /**
+     * Cancel all unprocessed messages for a given id.
+     *
+     * If a message has multiple listeners and one of those listeners has been alerted, the other
+     * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+     * that are still queued.
+     *
+     * @param id The message id to cancel.
+     */
+    void cancelMessages(int id);
+
+    /**
+     * Cancel all unprocessed messages for a given message type.
+     *
+     * If a message has multiple listeners and one of those listeners has been alerted, the other
+     * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+     * that are still queued.
+     *
+     * @param messageType The class of the message to cancel
+     */
+    <T> void cancelMessages(Class<T> messageType);
+
+    /**
+     * Add a listener for a message that does not handle any extra data.
+     *
+     * See also {@link #subscribeTo(Class, DataMessageListener)}.
+     *
+     * @param id The message id to listener for.
+     * @param listener
+     */
+    void subscribeTo(int id, SimpleMessageListener listener);
+
+    /**
+     * Add a listener for a message of a specific type.
+     *
+     * See also {@link #subscribeTo(Class, DataMessageListener)}.
+     *
+     * @param messageType The class of message to listen for.
+     * @param listener
+     */
+    <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener);
+
+    /**
+     * Remove a listener for a specific message.
+     *
+     * See also {@link #unsubscribeFrom(Class, DataMessageListener)}
+     *
+     * @param id The message id to stop listening for.
+     * @param listener The listener to remove.
+     */
+    void unsubscribeFrom(int id, SimpleMessageListener listener);
+
+    /**
+     * Remove a listener for a specific message.
+     *
+     * See also {@link #unsubscribeFrom(int, SimpleMessageListener)}.
+     *
+     * @param messageType The class of message to stop listening for.
+     * @param listener The listener to remove.
+     */
+    <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener);
+
+    /**
+     * Remove a listener for all messages that it is subscribed to.
+     *
+     * See also {@link #unsubscribeFrom(DataMessageListener)}.
+     *
+     * @param listener The listener to remove.
+     */
+    void unsubscribeFrom(SimpleMessageListener listener);
+
+    /**
+     * Remove a listener for all messages that it is subscribed to.
+     *
+     * See also {@link #unsubscribeFrom(SimpleMessageListener)}.
+     *
+     * @param listener The listener to remove.
+     */
+    <T> void unsubscribeFrom(DataMessageListener<T> listener);
+
+    /**
+     * A Listener interface for when no extra data is expected or desired.
+     */
+    interface SimpleMessageListener {
+        /** */
+        void onMessage(int id);
+    }
+
+    /**
+     * A Listener interface for when extra data is expected or desired.
+     *
+     * @param <T>
+     */
+    interface DataMessageListener<T> {
+        /** */
+        void onMessage(T data);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
new file mode 100644
index 0000000..7145c77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.concurrency;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of {@link MessageRouter}.
+ */
+public class MessageRouterImpl implements MessageRouter {
+
+    private final DelayableExecutor mDelayableExecutor;
+
+    private final Map<Integer, List<Runnable>> mIdMessageCancelers = new HashMap<>();
+    private final Map<Class<Object>, List<Runnable>> mDataMessageCancelers = new HashMap<>();
+    private final Map<Integer, List<SimpleMessageListener>> mSimpleMessageListenerMap =
+            new HashMap<>();
+    private final Map<Class<?>, List<DataMessageListener<Object>>> mDataMessageListenerMap =
+            new HashMap<>();
+
+    public MessageRouterImpl(DelayableExecutor delayableExecutor) {
+        mDelayableExecutor = delayableExecutor;
+    }
+
+    @Override
+    public void sendMessageDelayed(int id, long delayMs) {
+        addCanceler(id, mDelayableExecutor.executeDelayed(() -> onMessage(id), delayMs));
+    }
+
+    @Override
+    public void sendMessageDelayed(Object data, long delayMs) {
+        addCanceler((Class<Object>) data.getClass(), mDelayableExecutor.executeDelayed(
+                () -> onMessage(data), delayMs));
+    }
+
+    @Override
+    public void cancelMessages(int id) {
+        synchronized (mIdMessageCancelers) {
+            if (mIdMessageCancelers.containsKey(id)) {
+                for (Runnable canceler : mIdMessageCancelers.get(id)) {
+                    canceler.run();
+                }
+                // Remove, don't clear, otherwise this could look like a memory leak as
+                // more and more unique message ids are passed in.
+                mIdMessageCancelers.remove(id);
+            }
+        }
+    }
+
+    @Override
+    public <T> void cancelMessages(Class<T> messageType) {
+        synchronized (mDataMessageCancelers) {
+            if (mDataMessageCancelers.containsKey(messageType)) {
+                for (Runnable canceler : mDataMessageCancelers.get(messageType)) {
+                    canceler.run();
+                }
+                // Remove, don't clear, otherwise this could look like a memory leak as
+                // more and more unique message types are passed in.
+                mDataMessageCancelers.remove(messageType);
+            }
+        }
+    }
+
+    @Override
+    public void subscribeTo(int id, SimpleMessageListener listener) {
+        synchronized (mSimpleMessageListenerMap) {
+            mSimpleMessageListenerMap.putIfAbsent(id, new ArrayList<>());
+            mSimpleMessageListenerMap.get(id).add(listener);
+        }
+    }
+
+    @Override
+    public <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener) {
+        synchronized (mDataMessageListenerMap) {
+            mDataMessageListenerMap.putIfAbsent(messageType, new ArrayList<>());
+            mDataMessageListenerMap.get(messageType).add((DataMessageListener<Object>) listener);
+        }
+    }
+
+    @Override
+    public void unsubscribeFrom(int id, SimpleMessageListener listener) {
+        synchronized (mSimpleMessageListenerMap) {
+            if (mSimpleMessageListenerMap.containsKey(id)) {
+                mSimpleMessageListenerMap.get(id).remove(listener);
+            }
+        }
+    }
+
+    @Override
+    public <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener) {
+        synchronized (mDataMessageListenerMap) {
+            if (mDataMessageListenerMap.containsKey(messageType)) {
+                mDataMessageListenerMap.get(messageType).remove(listener);
+            }
+        }
+    }
+
+    @Override
+    public void unsubscribeFrom(SimpleMessageListener listener) {
+        synchronized (mSimpleMessageListenerMap) {
+            for (Integer id : mSimpleMessageListenerMap.keySet()) {
+                mSimpleMessageListenerMap.get(id).remove(listener);
+            }
+        }
+    }
+
+    @Override
+    public <T> void unsubscribeFrom(DataMessageListener<T> listener) {
+        synchronized (mDataMessageListenerMap) {
+            for (Class<?> messageType : mDataMessageListenerMap.keySet()) {
+                mDataMessageListenerMap.get(messageType).remove(listener);
+            }
+        }
+    }
+
+    private void addCanceler(int id, Runnable canceler) {
+        synchronized (mIdMessageCancelers) {
+            mIdMessageCancelers.putIfAbsent(id, new ArrayList<>());
+            mIdMessageCancelers.get(id).add(canceler);
+        }
+    }
+
+    private void addCanceler(Class<Object> data, Runnable canceler) {
+        synchronized (mDataMessageCancelers) {
+            mDataMessageCancelers.putIfAbsent(data, new ArrayList<>());
+            mDataMessageCancelers.get(data).add(canceler);
+        }
+    }
+
+    private void onMessage(int id) {
+        synchronized (mSimpleMessageListenerMap) {
+            if (mSimpleMessageListenerMap.containsKey(id)) {
+                for (SimpleMessageListener listener : mSimpleMessageListenerMap.get(id)) {
+                    listener.onMessage(id);
+                }
+            }
+        }
+
+        synchronized (mIdMessageCancelers) {
+            if (mIdMessageCancelers.containsKey(id) && !mIdMessageCancelers.get(id).isEmpty()) {
+                mIdMessageCancelers.get(id).remove(0);
+                if (mIdMessageCancelers.get(id).isEmpty()) {
+                    mIdMessageCancelers.remove(id);
+                }
+            }
+        }
+    }
+
+    private void onMessage(Object data) {
+        synchronized (mDataMessageListenerMap) {
+            if (mDataMessageListenerMap.containsKey(data.getClass())) {
+                for (DataMessageListener<Object> listener : mDataMessageListenerMap.get(
+                        data.getClass())) {
+                    listener.onMessage(data);
+                }
+            }
+        }
+
+        synchronized (mDataMessageCancelers) {
+            if (mDataMessageCancelers.containsKey(data.getClass())
+                    && !mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+                mDataMessageCancelers.get(data.getClass()).remove(0);
+                if (mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+                    mDataMessageCancelers.remove(data.getClass());
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index b9b20c7..e9e794e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -170,4 +170,20 @@
     public static Executor provideUiBackgroundExecutor() {
         return Executors.newSingleThreadExecutor();
     }
+
+    /** */
+    @Provides
+    @Main
+    public static MessageRouter providesMainMessageRouter(
+            @Main DelayableExecutor executor) {
+        return new MessageRouterImpl(executor);
+    }
+
+    /** */
+    @Provides
+    @Background
+    public static MessageRouter providesBackgroundMessageRouter(
+            @Background DelayableExecutor executor) {
+        return new MessageRouterImpl(executor);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index edea305..c799888 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -36,7 +36,6 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -60,6 +59,8 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSIconViewImpl;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -110,18 +111,18 @@
     private static final int DO_GARBAGE_INSPECTION = 1000;
     private static final int DO_HEAP_TRACK = 3000;
 
-    private static final int GARBAGE_ALLOWANCE = 5;
+    static final int GARBAGE_ALLOWANCE = 5;
 
     private static final String TAG = "GarbageMonitor";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final Handler mHandler;
+    private final MessageRouter mMessageRouter;
     private final TrackedGarbage mTrackedGarbage;
     private final LeakReporter mLeakReporter;
     private final Context mContext;
-    private final ActivityManager mAm;
+    private final DelayableExecutor mDelayableExecutor;
     private MemoryTile mQSTile;
-    private DumpTruck mDumpTruck;
+    private final DumpTruck mDumpTruck;
 
     private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
     private final ArrayList<Long> mPids = new ArrayList<>();
@@ -133,13 +134,16 @@
     @Inject
     public GarbageMonitor(
             Context context,
-            @Background Looper bgLooper,
+            @Background DelayableExecutor delayableExecutor,
+            @Background MessageRouter messageRouter,
             LeakDetector leakDetector,
             LeakReporter leakReporter) {
         mContext = context.getApplicationContext();
-        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
-        mHandler = new BackgroundHeapCheckHandler(bgLooper);
+        mDelayableExecutor = delayableExecutor;
+        mMessageRouter = messageRouter;
+        mMessageRouter.subscribeTo(DO_GARBAGE_INSPECTION, this::doGarbageInspection);
+        mMessageRouter.subscribeTo(DO_HEAP_TRACK, this::doHeapTrack);
 
         mTrackedGarbage = leakDetector.getTrackedGarbage();
         mLeakReporter = leakReporter;
@@ -158,13 +162,13 @@
             return;
         }
 
-        mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
+        mMessageRouter.sendMessage(DO_GARBAGE_INSPECTION);
     }
 
     public void startHeapTracking() {
         startTrackingProcess(
                 android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
-        mHandler.sendEmptyMessage(DO_HEAP_TRACK);
+        mMessageRouter.sendMessage(DO_HEAP_TRACK);
     }
 
     private boolean gcAndCheckGarbage() {
@@ -586,33 +590,18 @@
         }
     }
 
-    private class BackgroundHeapCheckHandler extends Handler {
-        BackgroundHeapCheckHandler(Looper onLooper) {
-            super(onLooper);
-            if (Looper.getMainLooper().equals(onLooper)) {
-                throw new RuntimeException(
-                        "BackgroundHeapCheckHandler may not run on the ui thread");
-            }
+    private void doGarbageInspection(int id) {
+        if (gcAndCheckGarbage()) {
+            mDelayableExecutor.executeDelayed(this::reinspectGarbageAfterGc, 100);
         }
 
-        @Override
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case DO_GARBAGE_INSPECTION:
-                    if (gcAndCheckGarbage()) {
-                        postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
-                    }
+        mMessageRouter.cancelMessages(DO_GARBAGE_INSPECTION);
+        mMessageRouter.sendMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
+    }
 
-                    removeMessages(DO_GARBAGE_INSPECTION);
-                    sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
-                    break;
-
-                case DO_HEAP_TRACK:
-                    update();
-                    removeMessages(DO_HEAP_TRACK);
-                    sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
-                    break;
-            }
-        }
+    private void doHeapTrack(int id) {
+        update();
+        mMessageRouter.cancelMessages(DO_HEAP_TRACK);
+        mMessageRouter.sendMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3320852..f317628 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -104,6 +104,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.view.RotationPolicy;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
@@ -400,7 +401,9 @@
         mDialog.setCanceledOnTouchOutside(true);
         mDialog.setOnShowListener(dialog -> {
             mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-            if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+            if (!shouldSlideInVolumeTray()) {
+                mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+            }
             mDialogView.setAlpha(0);
             mDialogView.animate()
                     .alpha(1)
@@ -587,6 +590,10 @@
         return (int) (alpha * 255);
     }
 
+    private boolean shouldSlideInVolumeTray() {
+        return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION;
+    }
+
     private boolean isLandscape() {
         return mContext.getResources().getConfiguration().orientation ==
                 Configuration.ORIENTATION_LANDSCAPE;
@@ -1320,7 +1327,7 @@
 
                     hideRingerDrawer();
                 }, 50));
-        if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f);
+        if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f);
         animator.start();
         checkODICaptionsTooltip(true);
         mController.notifyVisible(false);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 2216a91..3be1d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -38,6 +38,7 @@
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.tv.TvPipController;
 import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -144,10 +145,17 @@
 
     @WMSingleton
     @Provides
+    static PipTransitionState providePipTransitionState() {
+        return new PipTransitionState();
+    }
+
+    @WMSingleton
+    @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
             TvPipMenuController tvPipMenuController,
             SyncTransactionQueue syncTransactionQueue,
             PipBoundsState pipBoundsState,
+            PipTransitionState pipTransitionState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
             PipTransitionController pipTransitionController,
@@ -157,7 +165,7 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
-                syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+                syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
                 pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
                 shellTaskOrganizer, mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bc956dc..f2db4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -64,6 +64,7 @@
 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -104,7 +105,8 @@
 
     // Shell interfaces
     private final Optional<Pip> mPipOptional;
-    private final Optional<LegacySplitScreen> mSplitScreenOptional;
+    private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
     private final Optional<ShellCommandHandler> mShellCommandHandler;
@@ -120,6 +122,7 @@
     private final Executor mSysUiMainExecutor;
 
     private boolean mIsSysUiStateValid;
+    private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@@ -128,7 +131,8 @@
     @Inject
     public WMShell(Context context,
             Optional<Pip> pipOptional,
-            Optional<LegacySplitScreen> splitScreenOptional,
+            Optional<LegacySplitScreen> legacySplitScreenOptional,
+            Optional<SplitScreen> splitScreenOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutoutOptional,
             Optional<ShellCommandHandler> shellCommandHandler,
@@ -149,6 +153,7 @@
         mScreenLifecycle = screenLifecycle;
         mSysUiState = sysUiState;
         mPipOptional = pipOptional;
+        mLegacySplitScreenOptional = legacySplitScreenOptional;
         mSplitScreenOptional = splitScreenOptional;
         mOneHandedOptional = oneHandedOptional;
         mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
@@ -165,6 +170,7 @@
         mProtoTracer.add(this);
         mCommandQueue.addCallback(this);
         mPipOptional.ifPresent(this::initPip);
+        mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen);
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
@@ -218,8 +224,8 @@
     }
 
     @VisibleForTesting
-    void initSplitScreen(LegacySplitScreen legacySplitScreen) {
-        mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+    void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) {
+        mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
             @Override
             public void onKeyguardVisibilityChanged(boolean showing) {
                 // Hide the divider when keyguard is showing. Even though keyguard/statusbar is
@@ -229,6 +235,17 @@
                 legacySplitScreen.onKeyguardVisibilityChanged(showing);
             }
         };
+        mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback);
+    }
+
+    @VisibleForTesting
+    void initSplitScreen(SplitScreen splitScreen) {
+        mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+            @Override
+            public void onKeyguardOccludedChanged(boolean occluded) {
+                splitScreen.onKeyguardOccludedChanged(occluded);
+            }
+        };
         mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 6ef7450..7e733a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -194,11 +194,12 @@
             ShellTaskOrganizer organizer,
             DisplayController displayController,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler) {
+            @ShellMainThread Handler mainHandler,
+            SyncTransactionQueue syncQueue) {
         return Optional.of(BubbleController.create(context, null /* synchronizer */,
                 floatingContentCoordinator, statusBarService, windowManager,
                 windowManagerShellWrapper, launcherApps, taskStackListener,
-                uiEventLogger, organizer, displayController, mainExecutor, mainHandler));
+                uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
     }
 
     //
@@ -424,8 +425,9 @@
     @Provides
     static TaskViewFactoryController provideTaskViewFactoryController(
             ShellTaskOrganizer shellTaskOrganizer,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
+            @ShellMainThread ShellExecutor mainExecutor,
+            SyncTransactionQueue syncQueue) {
+        return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
     }
 
     //
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 36fd9be..be7813e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -48,6 +48,7 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransition;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -184,8 +185,15 @@
 
     @WMSingleton
     @Provides
+    static PipTransitionState providePipTransitionState() {
+        return new PipTransitionState();
+    }
+
+    @WMSingleton
+    @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
             SyncTransactionQueue syncTransactionQueue,
+            PipTransitionState pipTransitionState,
             PipBoundsState pipBoundsState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipMenuController menuPhoneController,
@@ -197,7 +205,7 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
-                syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+                syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
                 pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
                 shellTaskOrganizer, mainExecutor);
@@ -215,8 +223,9 @@
     static PipTransitionController providePipTransitionController(Context context,
             Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
             PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
-        return new PipTransition(context, pipBoundsState, pipMenuController,
+            PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
+            PhonePipMenuController pipMenuController) {
+        return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
                 pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 10ed1d7..ce02b83 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -19,6 +19,9 @@
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -247,4 +250,36 @@
 
         verify(plugin).setStyle(style);
     }
+
+    @Test
+    public void switchingToBigClock_makesSmallClockDisappear() {
+        mKeyguardClockSwitch.switchToClock(LARGE);
+
+        mKeyguardClockSwitch.mClockInAnim.end();
+        mKeyguardClockSwitch.mClockOutAnim.end();
+
+        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+    }
+
+    @Test
+    public void switchingToSmallClock_makesBigClockDisappear() {
+        mKeyguardClockSwitch.switchToClock(SMALL);
+
+        mKeyguardClockSwitch.mClockInAnim.end();
+        mKeyguardClockSwitch.mClockOutAnim.end();
+
+        assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+        assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+        // only big clock is removed at switch
+        assertThat(mLargeClockFrame.getParent()).isNull();
+        assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+    }
+
+    @Test
+    public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
+        assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
+        assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index ca857c5..64bdc2e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -19,11 +19,13 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 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;
@@ -33,6 +35,7 @@
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.MotionEvent;
 import android.view.WindowInsetsController;
 
 import androidx.test.filters.SmallTest;
@@ -43,6 +46,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -58,6 +62,7 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper()
 public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+    private static final int VIEW_WIDTH = 1600;
 
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
@@ -100,6 +105,8 @@
     private EmergencyButtonController mEmergencyButtonController;
     @Mock
     private Resources mResources;
+    @Mock
+    private FalsingCollector mFalsingCollector;
     private Configuration mConfiguration;
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -112,7 +119,9 @@
         mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
 
         when(mResources.getConfiguration()).thenReturn(mConfiguration);
+        when(mView.getContext()).thenReturn(mContext);
         when(mView.getResources()).thenReturn(mResources);
+        when(mView.getWidth()).thenReturn(VIEW_WIDTH);
         when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
                 .thenReturn(mAdminSecondaryLockScreenController);
         when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
@@ -131,7 +140,7 @@
                 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
-                mConfigurationController)
+                mConfigurationController, mFalsingCollector)
                 .create(mSecurityCallback);
     }
 
@@ -169,18 +178,156 @@
     public void onResourcesUpdate_callsThroughOnRotationChange() {
         // Rotation is the same, shouldn't cause an update
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView, times(0)).updateLayoutForSecurityMode(any());
+        verify(mView, times(0)).setOneHandedMode(anyBoolean());
 
         // Update rotation. Should trigger update
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
 
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView, times(1)).updateLayoutForSecurityMode(any());
+        verify(mView, times(1)).setOneHandedMode(anyBoolean());
     }
 
     @Test
-    public void updateKeyguardPosition_callsThroughToView() {
+    public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() {
+        when(mView.isOneHandedMode()).thenReturn(true);
+        mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f);
+        verify(mView).setOneHandedModeLeftAligned(true, false);
+
+        mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2);
+        verify(mView).setOneHandedModeLeftAligned(false, false);
+    }
+
+    @Test
+    public void updateKeyguardPosition_ignoredInTwoHandedMode() {
+        when(mView.isOneHandedMode()).thenReturn(false);
         mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
-        verify(mView).updateKeyguardPosition(1.0f);
+        verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean());
+    }
+
+    private void touchDownLeftSide() {
+        mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+                MotionEvent.obtain(
+                        /* downTime= */0,
+                        /* eventTime= */0,
+                        MotionEvent.ACTION_DOWN,
+                        /* x= */VIEW_WIDTH / 3f,
+                        /* y= */0,
+                        /* metaState= */0));
+    }
+
+    private void touchDownRightSide() {
+        mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+                MotionEvent.obtain(
+                        /* downTime= */0,
+                        /* eventTime= */0,
+                        MotionEvent.ACTION_DOWN,
+                        /* x= */(VIEW_WIDTH / 3f) * 2,
+                        /* y= */0,
+                        /* metaState= */0));
+    }
+
+    @Test
+    public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
+        when(mView.isOneHandedMode()).thenReturn(true);
+        when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
+
+        touchDownLeftSide();
+        verify(mFalsingCollector, never()).avoidGesture();
+
+        // Now on the right.
+        touchDownRightSide();
+        verify(mFalsingCollector).avoidGesture();
+
+        // Move and re-test
+        reset(mFalsingCollector);
+        when(mView.isOneHandedModeLeftAligned()).thenReturn(false);
+
+        // On the right...
+        touchDownRightSide();
+        verify(mFalsingCollector, never()).avoidGesture();
+
+        touchDownLeftSide();
+        verify(mFalsingCollector).avoidGesture();
+    }
+
+    @Test
+    public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() {
+        setUpKeyguardFlags(
+                /* deviceConfigCanUseOneHandedKeyguard= */false,
+                /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+        verify(mView).setOneHandedMode(false);
+    }
+
+    @Test
+    public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() {
+        setUpKeyguardFlags(
+                /* deviceConfigCanUseOneHandedKeyguard= */false,
+                /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+        verify(mView).setOneHandedMode(false);
+    }
+
+    @Test
+    public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() {
+        setUpKeyguardFlags(
+                /* deviceConfigCanUseOneHandedKeyguard= */true,
+                /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+        verify(mView).setOneHandedMode(false);
+    }
+
+    @Test
+    public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() {
+        setUpKeyguardFlags(
+                /* deviceConfigCanUseOneHandedKeyguard= */true,
+                /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+        verify(mView).setOneHandedMode(true);
+    }
+
+    @Test
+    public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() {
+        setUpKeyguardFlags(
+                /* deviceConfigCanUseOneHandedKeyguard= */true,
+                /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
+        verify(mView).setOneHandedMode(false);
+    }
+
+    private void setUpKeyguardFlags(
+            boolean deviceConfigCanUseOneHandedKeyguard,
+            boolean sysuiResourceCanUseOneHandedKeyguard) {
+        when(mResources.getBoolean(
+                com.android.internal.R.bool.config_enableDynamicKeyguardPositioning))
+                .thenReturn(deviceConfigCanUseOneHandedKeyguard);
+        when(mResources.getBoolean(
+                R.bool.can_use_one_handed_bouncer))
+                .thenReturn(sysuiResourceCanUseOneHandedKeyguard);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f5916e7..0276323 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -28,7 +28,6 @@
 import android.graphics.Insets;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.testing.TestableResources;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -37,8 +36,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -57,11 +54,6 @@
     private static final int FAKE_MEASURE_SPEC =
             View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
 
-    private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
-    private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
-
-
-
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
 
@@ -90,45 +82,8 @@
     }
 
     @Test
-    public void onMeasure_usesFullWidthWithoutOneHandedMode() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesHalfWidthWithFlagsEnabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                ONE_HANDED_SECURITY_MODE);
+    public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
+        mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true);
 
         int halfWidthMeasureSpec =
                 View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -138,11 +93,8 @@
     }
 
     @Test
-    public void onMeasure_usesFullWidthForFullScreenIme() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                TWO_HANDED_SECURITY_MODE);
+    public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
+        mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
         verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -153,10 +105,7 @@
         int imeInsetAmount = 100;
         int systemBarInsetAmount = 10;
 
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
+        mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -180,10 +129,7 @@
         int imeInsetAmount = 0;
         int systemBarInsetAmount = 10;
 
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
+        mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -201,56 +147,41 @@
         verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
     }
 
-    private void setupForUpdateKeyguardPosition(SecurityMode securityMode) {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                securityMode);
+    private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+        mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode);
+        mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
         mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
 
-        // Start off left-aligned. This should happen anyway, but just do this to ensure
-        // definitely move to the left.
-        mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
-
         // Clear any interactions with the mock so we know the interactions definitely come from the
         // below testing.
         reset(mSecurityViewFlipper);
     }
 
     @Test
-    public void updateKeyguardPosition_movesKeyguard() {
-        setupForUpdateKeyguardPosition(ONE_HANDED_SECURITY_MODE);
+    public void setIsLeftAligned_movesKeyguard() {
+        setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
 
-        mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+        mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+                /* leftAligned= */false, /* animate= */false);
         verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
 
-        mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+        mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+                /* leftAligned= */true, /* animate= */false);
         verify(mSecurityViewFlipper).setTranslationX(0.0f);
     }
 
     @Test
-    public void updateKeyguardPosition_doesntMoveTwoHandedKeyguard() {
-        setupForUpdateKeyguardPosition(TWO_HANDED_SECURITY_MODE);
+    public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() {
+        setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
 
-        mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+        mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+                /* leftAligned= */false, /* animate= */false);
         verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
 
-        mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+        mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+                /* leftAligned= */true, /* animate= */false);
         verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
     }
-
-    private void setUpKeyguard(
-            boolean deviceConfigCanUseOneHandedKeyguard,
-            boolean sysuiResourceCanUseOneHandedKeyguard,
-            SecurityMode securityMode) {
-        TestableResources testableResources = mContext.getOrCreateTestableResources();
-        testableResources.addOverride(
-                com.android.internal.R.bool.config_enableDynamicKeyguardPositioning,
-                deviceConfigCanUseOneHandedKeyguard);
-        testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
-                sysuiResourceCanUseOneHandedKeyguard);
-        mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
-    }
 }
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 5617f1b..fe5633e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -70,7 +70,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -90,7 +89,6 @@
 import java.util.List;
 
 @SmallTest
-@FlakyTest(bugId = 188890599)
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MagnificationModeSwitchTest extends SysuiTestCase {
@@ -507,6 +505,30 @@
                 expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
     }
 
+    @Test
+    public void onScreenSizeChanged_buttonIsShowingOnTheRightSide_expectedPosition() {
+        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds);
+        final float windowHeightFraction =
+                (float) (mWindowManager.getLayoutParamsFromAttachedView().y
+                        - oldDraggableBounds.top) / oldDraggableBounds.height();
+
+        // The window bounds and the draggable bounds are changed due to the screen size change.
+        final Rect tmpRect = new Rect(windowBounds);
+        tmpRect.scale(2);
+        final Rect newWindowBounds = new Rect(tmpRect);
+        mWindowManager.setWindowBounds(newWindowBounds);
+        mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+
+        final int expectedX = mMagnificationModeSwitch.mDraggableWindowBounds.right;
+        final int expectedY = (int) (windowHeightFraction
+                * mMagnificationModeSwitch.mDraggableWindowBounds.height())
+                + mMagnificationModeSwitch.mDraggableWindowBounds.top;
+        assertEquals(expectedX, mWindowManager.getLayoutParamsFromAttachedView().x);
+        assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
+    }
+
     private void assertModeUnchanged(int expectedMode) {
         final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
index 9621bed..8bb9d42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.accessibility;
 
-import static android.view.WindowInsets.Type.systemGestures;
-
-import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.view.Display;
@@ -79,14 +76,11 @@
 
     @Override
     public WindowMetrics getCurrentWindowMetrics() {
-        final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
-        final WindowInsets insets = new WindowInsets.Builder()
-                .setInsets(systemGestures(), systemGesturesInsets)
-                .build();
+        final WindowMetrics realMetrics = mWindowManager.getCurrentWindowMetrics();
         final WindowMetrics windowMetrics = new WindowMetrics(
-                mWindowBounds == null ? mWindowManager.getCurrentWindowMetrics().getBounds()
+                mWindowBounds == null ? realMetrics.getBounds()
                         : mWindowBounds,
-                mWindowInsets == null ? insets : mWindowInsets);
+                mWindowInsets == null ?  realMetrics.getWindowInsets() : mWindowInsets);
         return windowMetrics;
     }
 
@@ -106,10 +100,20 @@
         return (WindowManager.LayoutParams) mView.getLayoutParams();
     }
 
+    /**
+     * Sets the given window bounds to current window metrics.
+     *
+     * @param bounds the window bounds
+     */
     public void setWindowBounds(Rect bounds) {
         mWindowBounds = bounds;
     }
 
+    /**
+     * Sets the given window insets to the current window metics.
+     *
+     * @param insets the window insets.
+     */
     public void setWindowInsets(WindowInsets insets) {
         mWindowInsets = insets;
     }
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 f62069d..ca65330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import static android.view.Choreographer.FrameCallback;
+import static android.view.WindowInsets.Type.systemGestures;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -45,6 +46,8 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -55,11 +58,11 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -140,7 +143,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 188889181)
     public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -239,11 +241,13 @@
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
             mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
         });
     }
 
     @Test
-    public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() {
+    public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
         final Display display = Mockito.spy(mContext.getDisplay());
         when(display.getRotation()).thenReturn(Surface.ROTATION_90);
         when(mContext.getDisplay()).thenReturn(display);
@@ -251,13 +255,22 @@
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN);
         });
+        final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(),
+                mWindowMagnificationController.getCenterX());
+        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+        // Rotate the window 90 degrees.
+        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+                windowBounds.right);
+        mWindowManager.setWindowBounds(windowBounds);
 
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
         });
 
         assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
-        // The first invocation is called when the surface is created.
+        final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
+                mWindowMagnificationController.getCenterY());
+        assertEquals(expectedCenter, actualCenter);
         verify(mWindowManager, times(2)).updateViewLayout(any(), any());
     }
 
@@ -275,6 +288,33 @@
     }
 
     @Test
+    public void onScreenSizeChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
+        // The default position is at the center of the screen.
+        final float expectedRatio = 0.5f;
+        final Rect testWindowBounds = new Rect(
+                mWindowManager.getCurrentWindowMetrics().getBounds());
+        testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+                testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        mWindowManager.setWindowBounds(testWindowBounds);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+        });
+
+        // The ratio of center to window size should be the same.
+        assertEquals(expectedRatio,
+                mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
+                0);
+        assertEquals(expectedRatio,
+                mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
+                0);
+    }
+
+    @Test
     public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -419,9 +459,12 @@
     }
 
     @Test
-    public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
-        final WindowManager wm = mContext.getSystemService(WindowManager.class);
-        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+    public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        final WindowInsets testInsets = new WindowInsets.Builder()
+                .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
+                .build();
+        mWindowManager.setWindowInsets(testInsets);
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN);
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 ca11451..f53dddb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -198,7 +198,7 @@
                 mWindowManager,
                 mStatusBarStateController,
                 mFgExecutor,
-                mStatusBar,
+                Optional.of(mStatusBar),
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
                 mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index f62587c..57c57ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -49,6 +49,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -103,7 +104,7 @@
         mController = new UdfpsKeyguardViewController(
                 mView,
                 mStatusBarStateController,
-                mStatusBar,
+                Optional.of(mStatusBar),
                 mStatusBarKeyguardViewManager,
                 mKeyguardUpdateMonitor,
                 mExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
new file mode 100644
index 0000000..8c327af
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+
+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 CommunalHostViewControllerTest extends SysuiTestCase {
+    @Mock
+    private CommunalHostView mCommunalView;
+
+    private CommunalHostViewController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new CommunalHostViewController(mCommunalView);
+    }
+
+    @Test
+    public void testShow() {
+        mController.show(true /* visible */);
+        verify(mCommunalView).setVisibility(View.VISIBLE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 578c2d9..8c0d21e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -74,6 +74,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 @SmallTest
@@ -104,7 +105,6 @@
     @Mock private IWindowManager mWindowManager;
     @Mock private Executor mBackgroundExecutor;
     @Mock private UiEventLogger mUiEventLogger;
-    @Mock private GlobalActionsInfoProvider mInfoProvider;
     @Mock private RingerModeTracker mRingerModeTracker;
     @Mock private RingerModeLiveData mRingerModeLiveData;
     @Mock private SysUiState mSysUiState;
@@ -151,12 +151,11 @@
                 mWindowManager,
                 mBackgroundExecutor,
                 mUiEventLogger,
-                mInfoProvider,
                 mRingerModeTracker,
                 mSysUiState,
                 mHandler,
                 mPackageManager,
-                mStatusBar
+                Optional.of(mStatusBar)
         );
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
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 2fa67cc..6d1db37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -81,6 +81,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 import java.util.regex.Pattern;
 
@@ -169,7 +170,7 @@
                 mSysUiState,
                 mHandler,
                 mPackageManager,
-                mStatusBar
+                Optional.of(mStatusBar)
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
deleted file mode 100644
index 302a8d3..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.android.systemui.globalactions
-
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.res.Configuration
-import android.content.res.Resources
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.globalactions.GlobalActionsInfoProvider
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.SysuiTestCase
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class GlobalActionsInfoProviderTest : SysuiTestCase() {
-
-    @Mock private lateinit var walletClient: QuickAccessWalletClient
-    @Mock private lateinit var controlsController: ControlsController
-    @Mock private lateinit var activityStarter: ActivityStarter
-    @Mock private lateinit var mockContext: Context
-    @Mock private lateinit var mockResources: Resources
-    @Mock private lateinit var sharedPrefs: SharedPreferences
-    @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
-
-    private lateinit var infoProvider: GlobalActionsInfoProvider
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        mockContext = spy(context)
-        mockResources = spy(context.resources)
-        whenever(mockContext.resources).thenReturn(mockResources)
-        whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
-                .thenReturn(true)
-        whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt()))
-                .thenReturn(sharedPrefs)
-        whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
-        whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor)
-        whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean()))
-                .thenReturn(sharedPrefsEditor)
-
-        infoProvider = GlobalActionsInfoProvider(
-                mockContext,
-                walletClient,
-                controlsController,
-                activityStarter
-        )
-    }
-
-    @Test
-    fun testIsEligible_noCards() {
-        whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
-        whenever(walletClient.isWalletFeatureAvailable).thenReturn(false)
-
-        assertFalse(infoProvider.shouldShowMessage())
-    }
-
-    @Test
-    fun testIsEligible_hasCards() {
-        whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
-        whenever(walletClient.isWalletFeatureAvailable).thenReturn(true)
-
-        assertTrue(infoProvider.shouldShowMessage())
-    }
-
-    @Test
-    fun testNotEligible_shouldNotShow() {
-        whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
-                .thenReturn(false)
-
-        assertFalse(infoProvider.shouldShowMessage())
-    }
-
-    @Test
-    fun testTooManyButtons_doesNotAdd() {
-        val configuration = Configuration()
-        configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
-        whenever(mockResources.configuration).thenReturn(configuration)
-
-        val parent = mock(ViewGroup::class.java)
-        infoProvider.addPanel(mockContext, parent, 5, { })
-
-        verify(parent, never()).addView(anyObject(), anyInt())
-    }
-
-    @Test
-    fun testLimitTimesShown() {
-        whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4)
-
-        assertFalse(infoProvider.shouldShowMessage())
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 25ae67b..79b0dd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -16,13 +16,12 @@
 
 package com.android.systemui.media
 
+import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import android.widget.FrameLayout
-import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -52,8 +51,7 @@
     private lateinit var statusBarStateController: SysuiStatusBarStateController
     @Mock
     private lateinit var configurationController: ConfigurationController
-    @Mock
-    private lateinit var featureFlags: FeatureFlags
+
     @Mock
     private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
     @JvmField @Rule
@@ -77,7 +75,6 @@
             bypassController,
             statusBarStateController,
             notificationLockscreenUserManager,
-            featureFlags,
             context,
             configurationController
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index d2527c6..8fd2a32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -81,6 +81,8 @@
     private NavigationBar mDefaultNavBar;
     private NavigationBar mSecondaryNavBar;
 
+    private CommandQueue mCommandQueue = mock(CommandQueue.class);
+
     private static final int SECONDARY_DISPLAY = 1;
 
     @Before
@@ -99,11 +101,11 @@
                         mock(StatusBarStateController.class),
                         mock(SysUiState.class),
                         mock(BroadcastDispatcher.class),
-                        mock(CommandQueue.class),
+                        mCommandQueue,
                         Optional.of(mock(Pip.class)),
                         Optional.of(mock(LegacySplitScreen.class)),
                         Optional.of(mock(Recents.class)),
-                        () -> mock(StatusBar.class),
+                        () -> Optional.of(mock(StatusBar.class)),
                         mock(ShadeController.class),
                         mock(NotificationRemoteInputManager.class),
                         mock(NotificationShadeDepthController.class),
@@ -112,6 +114,8 @@
                         mock(UiEventLogger.class),
                         mock(NavigationBarOverlayController.class),
                         mock(ConfigurationController.class),
+                        mock(NavigationBarA11yHelper.class),
+                        mock(TaskbarDelegate.class),
                         mock(UserTracker.class)));
         initializeNavigationBars();
     }
@@ -275,4 +279,9 @@
 
         verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L));
     }
+
+    @Test
+    public void test3ButtonTaskbarFlagDisabledNoRegister() {
+        verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index eac68f6..a6ff2e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -31,9 +31,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import com.android.systemui.navigationbar.RotationButton;
-import com.android.systemui.navigationbar.RotationButtonController;
 import com.android.systemui.statusbar.policy.RotationLockController;
 
 import org.junit.Before;
@@ -46,7 +43,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NavigationBarRotationContextTest extends SysuiTestCase {
-    static final int RES_UNDEF = 0;
     static final int DEFAULT_ROTATE = 0;
 
     @Rule
@@ -63,10 +59,18 @@
         final View view = new View(mContext);
         mRotationButton = mock(RotationButton.class);
         mRotationButtonController = new RotationButtonController(mContext, 0, 0);
-        mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
+        mRotationButtonController.setRotationButton(mRotationButton,
+                new RotationButton.RotationButtonUpdatesCallback() {
+                    @Override
+                    public void onVisibilityChanged(boolean isVisible) {
+                    }
+
+                    @Override
+                    public void onPositionChanged() {
+                    }
+                });
         // Due to a mockito issue, only spy the object after setting the initial state
         mRotationButtonController = spy(mRotationButtonController);
-        final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
         doReturn(view).when(mRotationButton).getCurrentView();
         doReturn(true).when(mRotationButton).acceptRotationProposal();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 1968f7f..ed3c473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -279,7 +279,7 @@
                 Optional.of(mock(Pip.class)),
                 Optional.of(mock(LegacySplitScreen.class)),
                 Optional.of(mock(Recents.class)),
-                () -> mock(StatusBar.class),
+                () -> Optional.of(mock(StatusBar.class)),
                 mock(ShadeController.class),
                 mock(NotificationRemoteInputManager.class),
                 mock(NotificationShadeDepthController.class),
@@ -287,6 +287,7 @@
                 mHandler,
                 mock(NavigationBarOverlayController.class),
                 mUiEventLogger,
+                mock(NavigationBarA11yHelper.class),
                 mock(UserTracker.class)));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
new file mode 100644
index 0000000..0a20001
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -0,0 +1,127 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@SmallTest
+internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase)
+    : SysuiTestCase() {
+
+    private val calculator = FloatingRotationButtonPositionCalculator(
+        MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM
+    )
+
+    @Test
+    fun calculatePosition() {
+        val position = calculator.calculatePosition(
+            testCase.rotation,
+            testCase.taskbarVisible,
+            testCase.taskbarStashed
+        )
+
+        assertThat(position).isEqualTo(testCase.expectedPosition)
+    }
+
+    internal class TestCase(
+        val rotation: Int,
+        val taskbarVisible: Boolean,
+        val taskbarStashed: Boolean,
+        val expectedPosition: Position
+    ) {
+        override fun toString(): String =
+            "when rotation = $rotation, " +
+                "taskbarVisible = $taskbarVisible, " +
+                "taskbarStashed = $taskbarStashed - " +
+                "expected $expectedPosition"
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<TestCase> =
+            listOf(
+                TestCase(
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.LEFT,
+                        translationX = MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_90,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+                        translationX = -MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_180,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.TOP or Gravity.RIGHT,
+                        translationX = -MARGIN_DEFAULT,
+                        translationY = MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_270,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.TOP or Gravity.LEFT,
+                        translationX = MARGIN_DEFAULT,
+                        translationY = MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = true,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.LEFT,
+                        translationX = MARGIN_TASKBAR_LEFT,
+                        translationY = -MARGIN_TASKBAR_BOTTOM
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = true,
+                    taskbarStashed = true,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.LEFT,
+                        translationX = MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    rotation = Surface.ROTATION_90,
+                    taskbarVisible = true,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+                        translationX = -MARGIN_TASKBAR_LEFT,
+                        translationY = -MARGIN_TASKBAR_BOTTOM
+                    )
+                )
+            )
+
+        private const val MARGIN_DEFAULT = 10
+        private const val MARGIN_TASKBAR_LEFT = 20
+        private const val MARGIN_TASKBAR_BOTTOM = 30
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 3562032..e73e5ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -58,6 +58,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.time.Duration;
+import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 import dagger.Lazy;
@@ -89,7 +90,7 @@
     private IThermalEventListener mSkinThermalEventListener;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private CommandQueue mCommandQueue;
-    @Mock private Lazy<StatusBar> mStatusBarLazy;
+    @Mock private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     @Mock private StatusBar mStatusBar;
 
     @Before
@@ -98,7 +99,7 @@
         mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
         mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
 
-        when(mStatusBarLazy.get()).thenReturn(mStatusBar);
+        when(mStatusBarOptionalLazy.get()).thenReturn(Optional.of(mStatusBar));
 
         mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
 
@@ -688,7 +689,8 @@
     }
 
     private void createPowerUi() {
-        mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarLazy);
+        mPowerUI = new PowerUI(
+                mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarOptionalLazy);
         mPowerUI.mThermalService = mThermalServiceMock;
     }
 
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 c40977b..8c53091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -53,7 +53,6 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -92,8 +91,6 @@
     @Mock
     private MediaHost mQQSMediaHost;
     @Mock
-    private FeatureFlags mFeatureFlags;
-    @Mock
     private FalsingManager mFalsingManager;
 
     public QSFragmentTest() {
@@ -185,7 +182,6 @@
                 mQSMediaHost,
                 mQQSMediaHost,
                 mQsComponentFactory,
-                mFeatureFlags,
                 mFalsingManager);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 65e5f97..6ff5aa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -49,7 +49,6 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.animation.DisappearParameters;
 
 import org.junit.Before;
@@ -93,8 +92,6 @@
     @Mock
     PagedTileLayout mPagedTileLayout;
     @Mock
-    FeatureFlags mFeatureFlags;
-    @Mock
     Resources mResources;
     @Mock
     Configuration mConfiguration;
@@ -108,9 +105,9 @@
         protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
                 QSCustomizerController qsCustomizerController, MediaHost mediaHost,
                 MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-                DumpManager dumpManager, FeatureFlags featureFlags) {
+                DumpManager dumpManager) {
             super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
-                    qsLogger, dumpManager, featureFlags);
+                    qsLogger, dumpManager);
         }
 
         @Override
@@ -140,7 +137,7 @@
 
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
         mController.init();
         reset(mQSTileRevealController);
@@ -152,7 +149,7 @@
 
         QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
                 mQSTileHost, mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags) {
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
             @Override
             protected QSTileRevealController createTileRevealController() {
                 return mQSTileRevealController;
@@ -241,18 +238,17 @@
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
         when(mMediaHost.getVisible()).thenReturn(true);
 
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
         assertThat(mController.shouldUseHorizontalLayout()).isTrue();
 
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
         assertThat(mController.shouldUseHorizontalLayout()).isFalse();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index bf6c981..1a87975 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -42,7 +42,6 @@
 import com.android.systemui.settings.brightness.BrightnessController;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.settings.brightness.ToggleSlider;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.animation.DisappearParameters;
 
@@ -96,8 +95,6 @@
     @Mock
     PagedTileLayout mPagedTileLayout;
     FalsingManagerFake mFalsingManager = new FalsingManagerFake();
-    @Mock
-    FeatureFlags mFeatureFlags;
 
     private QSPanelController mController;
 
@@ -109,6 +106,7 @@
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
         when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
+        when(mQSPanel.getResources()).thenReturn(mContext.getResources());
         when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
         when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
         when(mToggleSliderViewControllerFactory.create(any(), any()))
@@ -123,7 +121,7 @@
                 mQSTileHost, mQSCustomizerController, true, mMediaHost,
                 mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
                 mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
-                mFalsingManager, mFeatureFlags
+                mFalsingManager
         );
 
         mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
deleted file mode 100644
index 4f88599..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ /dev/null
@@ -1,115 +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 org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-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;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.logging.QSLogger;
-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.util.Collections;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSPanelTest extends SysuiTestCase {
-
-    private TestableLooper mTestableLooper;
-    private QSPanel mQsPanel;
-    @Mock
-    private QSTileHost mHost;
-    @Mock
-    private QSTileImpl dndTile;
-    @Mock
-    private QSPanelControllerBase.TileRecord mDndTileRecord;
-    @Mock
-    private QSLogger mQSLogger;
-    private ViewGroup mParentView;
-    @Mock
-    private QSDetail.Callback mCallback;
-    @Mock
-    private QSTileView mQSTileView;
-    @Mock
-    private ActivityStarter mActivityStarter;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
-
-//        // Dependencies for QSSecurityFooter
-//        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
-//        mDependency.injectMockDependency(SecurityController.class);
-//        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-//        mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
-        mDndTileRecord.tile = dndTile;
-        mDndTileRecord.tileView = mQSTileView;
-
-        mTestableLooper.runWithLooper(() -> {
-            mQsPanel = new QSPanel(mContext, null);
-            mQsPanel.initialize();
-            mQsPanel.onFinishInflate();
-            // Provides a parent with non-zero size for QSPanel
-            mParentView = new FrameLayout(mContext);
-            mParentView.addView(mQsPanel);
-
-            when(dndTile.getTileSpec()).thenReturn("dnd");
-            when(mHost.getTiles()).thenReturn(Collections.emptyList());
-            when(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView);
-            mQsPanel.addTile(mDndTileRecord);
-            mQsPanel.setCallback(mCallback);
-        });
-    }
-
-    @Test
-    public void testOpenDetailsWithExistingTile_NoException() {
-        mTestableLooper.processAllMessages();
-        mQsPanel.openDetails(dndTile);
-        mTestableLooper.processAllMessages();
-
-        verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
-    }
-
-    @Test
-    public void testOpenDetailsWithNullParameter_NoException() {
-        mTestableLooper.processAllMessages();
-        mQsPanel.openDetails(null);
-        mTestableLooper.processAllMessages();
-
-        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
new file mode 100644
index 0000000..a83a5e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class QSPanelTest : SysuiTestCase() {
+    private lateinit var mTestableLooper: TestableLooper
+    private lateinit var mQsPanel: QSPanel
+
+    @Mock
+    private lateinit var mHost: QSTileHost
+
+    @Mock
+    private lateinit var dndTile: QSTileImpl<*>
+
+    @Mock
+    private lateinit var mDndTileRecord: TileRecord
+
+    @Mock
+    private lateinit var mQSLogger: QSLogger
+    private lateinit var mParentView: ViewGroup
+
+    @Mock
+    private lateinit var mCallback: QSDetail.Callback
+
+    @Mock
+    private lateinit var mQSTileView: QSTileView
+
+    @Before
+    @Throws(Exception::class)
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mTestableLooper = TestableLooper.get(this)
+
+        mDndTileRecord.tile = dndTile
+        mDndTileRecord.tileView = mQSTileView
+        mTestableLooper.runWithLooper {
+            mQsPanel = QSPanel(mContext, null)
+            mQsPanel.initialize()
+            // QSPanel inflates a footer inside of it, mocking it here
+            mQsPanel.addView(LinearLayout(mContext).apply { id = R.id.qs_footer })
+            mQsPanel.onFinishInflate()
+            mQsPanel.setSecurityFooter(View(mContext), false)
+            mQsPanel.setHeaderContainer(LinearLayout(mContext))
+            // Provides a parent with non-zero size for QSPanel
+            mParentView = FrameLayout(mContext).apply {
+                addView(mQsPanel)
+            }
+
+            whenever(dndTile.tileSpec).thenReturn("dnd")
+            whenever(mHost.tiles).thenReturn(emptyList())
+            whenever(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView)
+            mQsPanel.addTile(mDndTileRecord)
+            mQsPanel.setCallback(mCallback)
+        }
+    }
+
+    @Test
+    fun testOpenDetailsWithExistingTile_NoException() {
+        mTestableLooper.runWithLooper {
+            mQsPanel.openDetails(dndTile)
+        }
+
+        verify(mCallback).onShowingDetail(any(), anyInt(), anyInt())
+    }
+
+    @Test
+    fun testOpenDetailsWithNullParameter_NoException() {
+        mTestableLooper.runWithLooper {
+            mQsPanel.openDetails(null)
+        }
+
+        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt())
+    }
+
+    @Test
+    fun testSecurityFooter_appearsOnBottomOnSplitShade() {
+        mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
+        mQsPanel.switchSecurityFooter(true)
+
+        mTestableLooper.runWithLooper {
+            mQsPanel.isExpanded = true
+        }
+
+        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(2)
+    }
+
+    @Test
+    fun testSecurityFooter_appearsOnBottomIfPortrait() {
+        mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_PORTRAIT))
+        mQsPanel.switchSecurityFooter(false)
+
+        mTestableLooper.runWithLooper {
+            mQsPanel.isExpanded = true
+        }
+
+        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(2)
+    }
+
+    @Test
+    fun testSecurityFooter_appearsOnTopIfSmallScreenAndLandscape() {
+        mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
+        mQsPanel.switchSecurityFooter(false)
+
+        mTestableLooper.runWithLooper {
+            mQsPanel.isExpanded = true
+        }
+
+        // -1 means that it is part of the mHeaderContainer
+        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
+    }
+
+    private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
+            context.resources.configuration.apply { orientation = newOrientation }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
new file mode 100644
index 0000000..f8373ff
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.brightness.BrightnessController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.times
+import org.mockito.Mockito.never
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+class QuickQSBrightnessControllerTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var brightnessController: BrightnessController
+    @get:Rule
+    val mockito = MockitoJUnit.rule()
+
+    lateinit var quickQSBrightnessController: QuickQSBrightnessController
+
+    @Before
+    fun setUp() {
+        quickQSBrightnessController = QuickQSBrightnessController(
+                brightnessControllerFactory = { brightnessController })
+    }
+
+    @Test
+    fun testSliderIsShownWhenInitializedInSplitShade() {
+        quickQSBrightnessController.init(shouldUseSplitNotificationShade = true)
+
+        verify(brightnessController).showSlider()
+    }
+
+    @Test
+    fun testSliderIsShownWhenRefreshedInSplitShade() {
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+        verify(brightnessController, times(1)).showSlider()
+    }
+
+    @Test
+    fun testSliderIsHiddenWhenRefreshedInNonSplitShade() {
+        // needs to be shown first
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+
+        verify(brightnessController).hideSlider()
+    }
+
+    @Test
+    fun testSliderChangesVisibilityWhenRotating() {
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+        verify(brightnessController, times(1)).showSlider()
+
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+        verify(brightnessController, times(1)).hideSlider()
+    }
+
+    @Test
+    fun testCallbacksAreRegisteredOnlyOnce() {
+        // this flow simulates expanding shade in portrait...
+        quickQSBrightnessController.setListening(true)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+        // ... and rotating to landscape/split shade where slider is visible
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+        verify(brightnessController, times(1)).registerCallbacks()
+    }
+
+    @Test
+    fun testCallbacksAreRegisteredOnlyOnceWhenRotatingPhone() {
+        quickQSBrightnessController.setListening(true)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+        verify(brightnessController, times(1)).registerCallbacks()
+    }
+
+    @Test
+    fun testCallbacksAreNotRegisteredWhenSliderNotVisible() {
+        quickQSBrightnessController.setListening(true)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+
+        verify(brightnessController, never()).registerCallbacks()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 0604e1b..62ac72e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.qs
 
+import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
@@ -66,6 +66,8 @@
     private lateinit var tileView: QSTileView
     @Mock
     private lateinit var featureFlags: FeatureFlags
+    @Mock
+    private lateinit var quickQsBrightnessController: QuickQSBrightnessController
 
     private lateinit var controller: QuickQSPanelController
 
@@ -75,6 +77,7 @@
 
         `when`(quickQSPanel.tileLayout).thenReturn(tileLayout)
         `when`(quickQSPanel.dumpableTag).thenReturn("")
+        `when`(quickQSPanel.resources).thenReturn(mContext.resources)
         `when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
 
         controller = QuickQSPanelController(
@@ -87,7 +90,7 @@
                 uiEventLogger,
                 qsLogger,
                 dumpManager,
-                featureFlags
+                quickQsBrightnessController
         )
 
         controller.init()
@@ -117,4 +120,4 @@
 
         verify(quickQSPanel, times(limit)).addTile(any())
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 35360bd..8b7e20e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -36,6 +36,8 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.VariableDateView
+import com.android.systemui.statusbar.policy.VariableDateViewController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -87,8 +89,14 @@
     @Mock
     private lateinit var privacyDialogController: PrivacyDialogController
     @Mock
+    private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
+    @Mock
+    private lateinit var variableDateViewController: VariableDateViewController
+    @Mock
     private lateinit var clock: Clock
     @Mock
+    private lateinit var variableDateView: VariableDateView
+    @Mock
     private lateinit var mockView: View
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private lateinit var context: Context
@@ -109,6 +117,8 @@
         stubViews()
         `when`(iconContainer.context).thenReturn(context)
         `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+        `when`(variableDateViewControllerFactory.create(any()))
+                .thenReturn(variableDateViewController)
         `when`(view.resources).thenReturn(mContext.resources)
         `when`(view.isAttachedToWindow).thenReturn(true)
         `when`(view.context).thenReturn(context)
@@ -133,7 +143,8 @@
                 colorExtractor,
                 privacyDialogController,
                 qsExpansionPathInterpolator,
-                featureFlags
+                featureFlags,
+                variableDateViewControllerFactory
         )
     }
 
@@ -274,6 +285,8 @@
         `when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
         `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
         `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
+        `when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
+        `when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
     }
 
     private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d44a526..e939411 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -17,6 +17,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertEquals;
 
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
@@ -327,4 +328,77 @@
         assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
         assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
     }
+
+    @Test
+    public void testExpandView_wifiNotConnected() {
+        mCastTile.refreshState();
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_wifiEnabledNotCasting() {
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_casting_projection() {
+        CastController.CastDevice device = new CastController.CastDevice();
+        device.state = CastController.CastDevice.STATE_CONNECTED;
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(device);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_connecting_projection() {
+        CastController.CastDevice connecting = new CastController.CastDevice();
+        connecting.state = CastDevice.STATE_CONNECTING;
+        connecting.name = "Test Casting Device";
+
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(connecting);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_casting_mediaRoute() {
+        CastController.CastDevice device = new CastController.CastDevice();
+        device.state = CastDevice.STATE_CONNECTED;
+        device.tag = mock(MediaRouter.RouteInfo.class);
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(device);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_connecting_mediaRoute() {
+        CastController.CastDevice connecting = new CastController.CastDevice();
+        connecting.state = CastDevice.STATE_CONNECTING;
+        connecting.tag = mock(RouteInfo.class);
+        connecting.name = "Test Casting Device";
+
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(connecting);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
new file mode 100644
index 0000000..a8f6f53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -0,0 +1,102 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class InternetAdapterTest extends SysuiTestCase {
+
+    private static final String WIFI_TITLE = "Wi-Fi Title";
+    private static final String WIFI_SUMMARY = "Wi-Fi Summary";
+    private InternetDialogController mInternetDialogController = mock(
+            InternetDialogController.class);
+    private InternetAdapter mInternetAdapter;
+    private InternetAdapter.InternetViewHolder mViewHolder;
+    @Mock
+    private WifiEntry mWifiEntry = mock(WifiEntry.class);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mInternetAdapter = new InternetAdapter(mInternetDialogController);
+        mViewHolder = (InternetAdapter.InternetViewHolder) mInternetAdapter
+                .onCreateViewHolder(new LinearLayout(mContext), 0);
+        when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
+    }
+
+    @Test
+    public void getItemCount_withApmOnWifiOnNoConnectedWifi_returnFour() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+        assertThat(mInternetAdapter.getItemCount()).isEqualTo(4);
+    }
+
+    @Test
+    public void getItemCount_withApmOnWifiOnHasConnectedWifi_returnThree() {
+        when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+        assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
+    }
+
+    @Test
+    public void getItemCount_withApmOffWifiOnNoConnectedWifi_returnThree() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+        assertThat(mInternetAdapter.getItemCount()).isEqualTo(3);
+    }
+
+    @Test
+    public void getItemCount_withApmOffWifiOnHasConnectedWifi_returnTwo() {
+        when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+        assertThat(mInternetAdapter.getItemCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
new file mode 100644
index 0000000..facb19f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -0,0 +1,306 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogControllerTest extends SysuiTestCase {
+
+    private static final int SUB_ID = 1;
+    private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
+    private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
+
+    private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+    private MockInternetDialogController mInternetDialogController;
+    private InternetDialogController.InternetDialogCallback mCallback =
+            mock(InternetDialogController.InternetDialogCallback.class);
+    private ActivityStarter mStarter = mock(ActivityStarter.class);
+    private WifiManager mWifiManager = mock(WifiManager.class);
+    private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
+    private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+    private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+    @Mock
+    private Handler mHandler;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
+    private GlobalSettings mGlobalSettings;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private NetworkController.AccessPointController mAccessPointController;
+    @Mock
+    private WifiEntry mConnectedEntry;
+    @Mock
+    private WifiInfo mWifiInfo;
+    @Mock
+    private ServiceState mServiceState;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+        when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
+        mInternetDialogController = new MockInternetDialogController(mContext, mUiEventLogger,
+                mStarter, mAccessPointController, mSubscriptionManager, mTelephonyManager,
+                mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
+                mKeyguardUpdateMonitor, mGlobalSettings);
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+                mInternetDialogController.mOnSubscriptionsChangedListener);
+        mInternetDialogController.onStart(mCallback);
+        mInternetDialogController.mActivityStarter = mActivityStarter;
+        mInternetDialogController.mConnectedEntry = mConnectedEntry;
+    }
+
+    @Test
+    public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
+        mInternetDialogController.setAirplaneModeEnabled(true);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+                getResourcesString("airplane_mode")));
+    }
+
+    @Test
+    public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+                getResourcesString("quick_settings_internet_label")));
+    }
+
+    @Test
+    public void getSubtitleText_withAirplaneModeOn_returnNull() {
+        mInternetDialogController.setAirplaneModeEnabled(true);
+
+        assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+    }
+
+    @Test
+    public void getSubtitleText_withWifiOff_returnWifiIsOff() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("wifi_is_off")));
+    }
+
+    @Test
+    public void getSubtitleText_withWifiOn_returnSearchWifi() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(true),
+                getResourcesString("wifi_empty_list_wifi_on")));
+    }
+
+    @Test
+    public void getSubtitleText_withWifiEntry_returnTapToConnect() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        List<ScanResult> wifiScanResults = mock(ArrayList.class);
+        doReturn(1).when(wifiScanResults).size();
+        when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("tap_a_network_to_connect")));
+    }
+
+    @Test
+    public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        List<ScanResult> wifiScanResults = new ArrayList<>();
+        doReturn(wifiScanResults).when(mWifiManager).getScanResults();
+        when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+        when(mSubscriptionManager.getActiveSubscriptionIdList())
+                .thenReturn(new int[] {SUB_ID});
+
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+        doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+        doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("all_network_unavailable")));
+    }
+
+    @Test
+    public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        List<ScanResult> wifiScanResults = new ArrayList<>();
+        doReturn(wifiScanResults).when(mWifiManager).getScanResults();
+        when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
+        when(mSubscriptionManager.getActiveSubscriptionIdList())
+                .thenReturn(new int[] {SUB_ID});
+
+        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+        doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+
+        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("non_carrier_network_unavailable")));
+    }
+
+    @Test
+    public void getConnectedWifiTitle_withNoConnectedEntry_returnNull() {
+        mInternetDialogController.mConnectedEntry = null;
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
+                ""));
+    }
+
+    @Test
+    public void getConnectedWifiTitle_withConnectedEntry_returnTitle() {
+        when(mConnectedEntry.getTitle()).thenReturn(CONNECTED_TITLE);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
+                CONNECTED_TITLE));
+    }
+
+    @Test
+    public void getConnectedWifiSummary_withNoConnectedEntry_returnNull() {
+        mInternetDialogController.mConnectedEntry = null;
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
+                ""));
+    }
+
+    @Test
+    public void getConnectedWifiSummary_withConnectedEntry_returnSummary() {
+        when(mConnectedEntry.getSummary(false)).thenReturn(CONNECTED_SUMMARY);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
+                CONNECTED_SUMMARY));
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withNoConnectedEntry_returnNull() {
+        mInternetDialogController.mConnectedEntry = null;
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withNoConnectedEntryKey_returnNull() {
+        when(mConnectedEntry.getKey()).thenReturn(null);
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withConnectedEntryKey_returnIntent() {
+        when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNotNull();
+    }
+
+    @Test
+    public void launchWifiNetworkDetailsSetting_withNoConnectedEntry_doNothing() {
+        mInternetDialogController.mConnectedEntry = null;
+
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+        verify(mActivityStarter, never())
+                .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+    }
+
+    @Test
+    public void launchWifiNetworkDetailsSetting_withConnectedEntryKey_startActivity() {
+        when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+    }
+
+    private String getResourcesString(String name) {
+        return mContext.getResources().getString(getResourcesId(name));
+    }
+
+    private int getResourcesId(String name) {
+        return mContext.getResources().getIdentifier(name, "string",
+                mContext.getPackageName());
+    }
+
+    private class MockInternetDialogController extends InternetDialogController {
+
+        private GlobalSettings mGlobalSettings;
+        private boolean mIsAirplaneModeOn;
+
+        MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
+                ActivityStarter starter, AccessPointController accessPointController,
+                SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+                @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+                @Main Handler handler, @Main Executor mainExecutor,
+                BroadcastDispatcher broadcastDispatcher,
+                KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings) {
+            super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
+                    telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
+                    broadcastDispatcher, keyguardUpdateMonitor, globalSettings);
+            mGlobalSettings = globalSettings;
+        }
+
+        @Override
+        boolean isAirplaneModeEnabled() {
+            return mIsAirplaneModeOn;
+        }
+
+        public void setAirplaneModeEnabled(boolean enabled) {
+            mIsAirplaneModeOn = enabled;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
new file mode 100644
index 0000000..94bd959
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -0,0 +1,246 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wifitrackerlib.WifiEntry;
+
+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 java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogTest extends SysuiTestCase {
+
+    private static final int SUB_ID = 1;
+    private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+    private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+    private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+    private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+    private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+
+    private InternetDialogFactory mInternetDialogFactory = mock(InternetDialogFactory.class);
+    private InternetAdapter mInternetAdapter = mock(InternetAdapter.class);
+    private InternetDialogController mInternetDialogController = mock(
+            InternetDialogController.class);
+    private InternetDialogController.InternetDialogCallback mCallback =
+            mock(InternetDialogController.InternetDialogCallback.class);
+    private MockInternetDialog mInternetDialog;
+    private WifiReceiver mWifiReceiver = null;
+    private WifiManager mMockWifiManager = mock(WifiManager.class);
+    private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+    @Mock
+    private WifiEntry mWifiEntry = mock(WifiEntry.class);
+    @Mock
+    private WifiInfo mWifiInfo;
+    @Mock
+    private Handler mHandler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mInternetDialog = new MockInternetDialog(mContext, mInternetDialogFactory,
+                mInternetDialogController, true, true, mUiEventLogger, mHandler);
+        mInternetDialog.show();
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
+        when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+        when(mMockWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
+        mInternetDialog.setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
+        mInternetDialog.setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
+        mInternetDialog.setConnectedWifiTitle(WIFI_TITLE);
+        mInternetDialog.setConnectedWifiSummary(WIFI_SUMMARY);
+        mWifiReceiver = new WifiReceiver();
+        IntentFilter mIntentFilter = new IntentFilter();
+        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        mContext.registerReceiver(mWifiReceiver, mIntentFilter);
+        when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mInternetDialogController.getWifiEntryList()).thenReturn(Arrays.asList(mWifiEntry));
+    }
+
+    @After
+    public void tearDown() {
+        mInternetDialog.dismissDialog();
+    }
+
+    @Test
+    public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialog.updateDialog();
+        final TextView view = mInternetDialog.mDialogView.requireViewById(
+                R.id.internet_dialog_subtitle);
+
+        assertThat(view.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+        mInternetDialog.updateDialog();
+        final TextView view = mInternetDialog.mDialogView.requireViewById(
+                R.id.internet_dialog_subtitle);
+
+        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_withApmOn_mobileDataLayoutGone() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialog.updateDialog();
+        final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+                R.id.mobile_network_layout);
+
+        assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withWifiOnAndHasConnectedWifi_connectedWifiLayoutVisible() {
+        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+        when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mWifiEntry.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mInternetDialogController.getConnectedWifiEntry()).thenReturn(mWifiEntry);
+        mInternetDialog.updateDialog();
+        final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+                R.id.wifi_connected_layout);
+
+        assertThat(linearLayout.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_withWifiOnAndNoConnectedWifi_connectedWifiLayoutGone() {
+        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+        mInternetDialog.updateDialog();
+        final LinearLayout linearLayout = mInternetDialog.mDialogView.requireViewById(
+                R.id.wifi_connected_layout);
+
+        assertThat(linearLayout.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withWifiOff_WifiRecycleViewGone() {
+        when(mMockWifiManager.isWifiEnabled()).thenReturn(false);
+        mInternetDialog.updateDialog();
+        final RecyclerView view = mInternetDialog.mDialogView.requireViewById(
+                R.id.wifi_list_layout);
+
+        assertThat(view.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onClickSeeMoreButton_clickSeeMore_verifyLaunchNetworkSetting() {
+        final LinearLayout seeAllLayout = mInternetDialog.mDialogView.requireViewById(
+                R.id.see_all_layout);
+        seeAllLayout.performClick();
+
+        verify(mInternetDialogController).launchNetworkSetting();
+    }
+
+    private class MockInternetDialog extends InternetDialog {
+
+        private String mMobileNetworkTitle;
+        private String mMobileNetworkSummary;
+        private String mConnectedWifiTitle;
+        private String mConnectedWifiSummary;
+
+        MockInternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+                InternetDialogController internetDialogController, boolean canConfigMobileData,
+                boolean aboveStatusBar, UiEventLogger uiEventLogger, @Main Handler handler) {
+            super(context, internetDialogFactory, internetDialogController, canConfigMobileData,
+                    aboveStatusBar, uiEventLogger, handler);
+            mAdapter = mInternetAdapter;
+            mWifiManager = mMockWifiManager;
+        }
+
+        @Override
+        String getMobileNetworkTitle() {
+            return mMobileNetworkTitle;
+        }
+
+        @Override
+        String getMobileNetworkSummary() {
+            return mMobileNetworkSummary;
+        }
+
+        void setMobileNetworkTitle(String title) {
+            mMobileNetworkTitle = title;
+        }
+
+        void setMobileNetworkSummary(String summary) {
+            mMobileNetworkSummary = summary;
+        }
+
+        @Override
+        String getConnectedWifiTitle() {
+            return mConnectedWifiTitle;
+        }
+
+        @Override
+        String getConnectedWifiSummary() {
+            return mConnectedWifiSummary;
+        }
+
+        void setConnectedWifiTitle(String title) {
+            mConnectedWifiTitle = title;
+        }
+
+        void setConnectedWifiSummary(String summary) {
+            mConnectedWifiSummary = summary;
+        }
+
+        @Override
+        public void onWifiStateReceived(Context context, Intent intent) {
+            setMobileNetworkTitle(MOBILE_NETWORK_TITLE);
+            setMobileNetworkSummary(MOBILE_NETWORK_SUMMARY);
+        }
+    }
+
+    private class WifiReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                return;
+            }
+
+            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                mInternetDialog.updateDialog();
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 21c6292..d5a2919 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.os.Bundle;
+import android.view.InsetsState;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
 
@@ -124,24 +125,25 @@
     public void testOnSystemBarAttributesChanged() {
         doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1,
                 new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
-                BEHAVIOR_DEFAULT, false);
+                BEHAVIOR_DEFAULT, new InsetsState(), "test");
     }
 
     @Test
     public void testOnSystemBarAttributesChangedForSecondaryDisplay() {
         doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1,
                 new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
-                BEHAVIOR_DEFAULT, false);
+                BEHAVIOR_DEFAULT, new InsetsState(), "test");
     }
 
     private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, boolean isFullscreen) {
+            @Behavior int behavior, InsetsState requestedState, String packageName) {
         mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
-                navbarColorManagedByIme, behavior, isFullscreen);
+                navbarColorManagedByIme, behavior, requestedState, packageName);
         waitForIdleSync();
         verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance),
-                eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen));
+                eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior),
+                eq(requestedState), eq(packageName));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 18cf1c8..c50296b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,10 +1,10 @@
 package com.android.systemui.statusbar
 
+import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.util.DisplayMetrics
-import androidx.test.filters.SmallTest
 import com.android.systemui.ExpandHelper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
@@ -67,7 +67,6 @@
     @Mock lateinit var falsingManager: FalsingManager
     @Mock lateinit var notificationPanelController: NotificationPanelViewController
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
-    @Mock lateinit var featureFlags: FeatureFlags
     @Mock lateinit var depthController: NotificationShadeDepthController
     @Mock lateinit var stackscroller: NotificationStackScrollLayout
     @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
@@ -92,11 +91,10 @@
             displayMetrics = displayMetrics,
             mediaHierarchyManager = mediaHierarchyManager,
             scrimController = scrimController,
-            featureFlags = featureFlags,
+            depthController = depthController,
             context = context,
             configurationController = configurationController,
-            falsingManager = falsingManager,
-            depthController = depthController
+            falsingManager = falsingManager
         )
         whenever(nsslController.view).thenReturn(stackscroller)
         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 8e949e7..8b7c76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -44,6 +44,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 import dagger.Lazy;
 
 @SmallTest
@@ -80,7 +82,7 @@
 
         mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext,
                 mLockscreenUserManager, mSmartReplyController, mEntryManager,
-                () -> mock(StatusBar.class),
+                () -> Optional.of(mock(StatusBar.class)),
                 mStateController,
                 Handler.createAsync(Looper.myLooper()),
                 mRemoteInputUriController,
@@ -265,7 +267,7 @@
                 NotificationLockscreenUserManager lockscreenUserManager,
                 SmartReplyController smartReplyController,
                 NotificationEntryManager notificationEntryManager,
-                Lazy<StatusBar> statusBarLazy,
+                Lazy<Optional<StatusBar>> statusBarOptionalLazy,
                 StatusBarStateController statusBarStateController,
                 Handler mainHandler,
                 RemoteInputUriController remoteInputUriController,
@@ -276,7 +278,7 @@
                     lockscreenUserManager,
                     smartReplyController,
                     notificationEntryManager,
-                    statusBarLazy,
+                    statusBarOptionalLazy,
                     statusBarStateController,
                     mainHandler,
                     remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 7cbc4e4..659d96e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
@@ -90,7 +92,7 @@
 
         mRemoteInputManager = new NotificationRemoteInputManager(mContext,
                 mock(NotificationLockscreenUserManager.class), mSmartReplyController,
-                mNotificationEntryManager, () -> mock(StatusBar.class),
+                mNotificationEntryManager, () -> Optional.of(mock(StatusBar.class)),
                 mStatusBarStateController,
                 Handler.createAsync(Looper.myLooper()),
                 mRemoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9f537f5..fc44669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -91,7 +91,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
@@ -99,8 +98,6 @@
 
 import java.util.Optional;
 
-import javax.inject.Provider;
-
 /**
  * Tests for {@link NotificationGutsManager}.
  */
@@ -157,11 +154,12 @@
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
 
         mGutsManager = new NotificationGutsManager(mContext,
-                () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
-                mINotificationManager, mNotificationEntryManager, mPeopleSpaceWidgetManager,
-                mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker,
-                mAssistantFeedbackController, Optional.of(mBubblesManager),
-                new UiEventLoggerFake(), mOnUserInteractionCallback, mShadeController);
+                () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager,
+                mHighPriorityProvider, mINotificationManager, mNotificationEntryManager,
+                mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
+                mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
+                Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
+                mShadeController);
         mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
                 mCheckSaveListener, mOnSettingsClickListener);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
new file mode 100644
index 0000000..6ee2f20
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -0,0 +1,439 @@
+/*
+ * 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.notification.stack;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+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.content.res.Resources;
+import android.metrics.LogMaker;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tuner.TunerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link NotificationStackScrollLayoutController}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
+
+    @Mock private NotificationGutsManager mNotificationGutsManager;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
+    @Mock private TunerService mTunerService;
+    @Mock private DynamicPrivacyController mDynamicPrivacyController;
+    @Mock private ConfigurationController mConfigurationController;
+    @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
+    @Mock private ZenModeController mZenModeController;
+    @Mock private KeyguardMediaController mKeyguardMediaController;
+    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
+    @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private SysuiColorExtractor mColorExtractor;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private Resources mResources;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
+    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
+    @Mock private StatusBar mStatusBar;
+    @Mock private ScrimController mScrimController;
+    @Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
+    @Mock private SectionHeaderController mSilentHeaderController;
+    @Mock private FeatureFlags mFeatureFlags;
+    @Mock private NotifPipeline mNotifPipeline;
+    @Mock private NotifCollection mNotifCollection;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private IStatusBarService mIStatusBarService;
+    @Mock private UiEventLogger mUiEventLogger;
+    @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
+    @Mock private ForegroundServiceSectionController mFgServicesSectionController;
+    @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
+    @Mock private LayoutInflater mLayoutInflater;
+    @Mock private NotificationRemoteInputManager mRemoteInputManager;
+    @Mock private RemoteInputController mRemoteInputController;
+    @Mock private VisualStabilityManager mVisualStabilityManager;
+    @Mock private ShadeController mShadeController;
+
+    @Captor
+    private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
+
+    private NotificationStackScrollLayoutController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
+        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
+        when(mFgServicesSectionController.createView(mLayoutInflater))
+                .thenReturn(mForegroundServiceDungeonView);
+        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
+        mController = new NotificationStackScrollLayoutController(
+                true,
+                mNotificationGutsManager,
+                mHeadsUpManager,
+                mNotificationRoundnessManager,
+                mTunerService,
+                mDynamicPrivacyController,
+                mConfigurationController,
+                mSysuiStatusBarStateController,
+                mKeyguardMediaController,
+                mKeyguardBypassController,
+                mZenModeController,
+                mColorExtractor,
+                mNotificationLockscreenUserManager,
+                mMetricsLogger,
+                new FalsingCollectorFake(),
+                new FalsingManagerFake(),
+                mResources,
+                mNotificationSwipeHelperBuilder,
+                mStatusBar,
+                mScrimController,
+                mLegacyGroupManager,
+                mLegacyGroupManager,
+                mSilentHeaderController,
+                mFeatureFlags,
+                mNotifPipeline,
+                mNotifCollection,
+                mEntryManager,
+                mLockscreenShadeTransitionController,
+                mIStatusBarService,
+                mUiEventLogger,
+                mFgFeatureController,
+                mFgServicesSectionController,
+                mLayoutInflater,
+                mRemoteInputManager,
+                mVisualStabilityManager,
+                mShadeController
+        );
+
+        when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
+    }
+
+    @Test
+    public void testAttach_viewAlreadyAttached() {
+        mController.attach(mNotificationStackScrollLayout);
+
+        verify(mConfigurationController).addCallback(
+                any(ConfigurationController.ConfigurationListener.class));
+    }
+    @Test
+    public void testAttach_viewAttachedAfterInit() {
+        when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(false);
+
+        mController.attach(mNotificationStackScrollLayout);
+
+        verify(mConfigurationController, never()).addCallback(
+                any(ConfigurationController.ConfigurationListener.class));
+
+        mController.mOnAttachStateChangeListener.onViewAttachedToWindow(
+                mNotificationStackScrollLayout);
+
+        verify(mConfigurationController).addCallback(
+                any(ConfigurationController.ConfigurationListener.class));
+    }
+
+    @Test
+    public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() {
+        mController.attach(mNotificationStackScrollLayout);
+        mController.mConfigurationListener.onDensityOrFontScaleChanged();
+        verify(mNotificationStackScrollLayout).reinflateViews();
+    }
+
+    @Test
+    public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
+        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
+
+        setupShowEmptyShadeViewState(stateListener, true);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ true,
+                /* notifVisibleInShade= */ true);
+
+        setupShowEmptyShadeViewState(stateListener, false);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ false,
+                /* notifVisibleInShade= */ true);
+    }
+
+    @Test
+    public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
+        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
+
+        setupShowEmptyShadeViewState(stateListener, true);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ true,
+                /* notifVisibleInShade= */ false);
+
+        setupShowEmptyShadeViewState(stateListener, false);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ false,
+                /* notifVisibleInShade= */ false);
+    }
+
+    @Test
+    public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
+        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
+        when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
+        stateListener.onStateChanged(SHADE);
+        mController.getView().removeAllViews();
+
+        mController.setQsExpanded(false);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ true,
+                /* notifVisibleInShade= */ false);
+
+        mController.setQsExpanded(true);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
+        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+                /* visible= */ true,
+                /* notifVisibleInShade= */ false);
+    }
+
+    @Test
+    public void testOnUserChange_verifySensitiveProfile() {
+        when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
+
+        ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
+                .forClass(UserChangedListener.class);
+
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mNotificationLockscreenUserManager)
+                .addUserChangedListener(userChangedCaptor.capture());
+        reset(mNotificationStackScrollLayout);
+
+        UserChangedListener changedListener = userChangedCaptor.getValue();
+        changedListener.onUserChanged(0);
+        verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
+    }
+
+    @Test
+    public void testOnStatePostChange_verifyIfProfileIsPublic() {
+        when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
+
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
+
+        stateListener.onStatePostChange();
+        verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
+    }
+
+    @Test
+    public void testOnMenuShownLogging() {
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+        when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
+                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+        ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
+                ArgumentCaptor.forClass(OnMenuEventListener.class);
+
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
+                onMenuEventListenerArgumentCaptor.capture());
+
+        OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
+
+        onMenuEventListener.onMenuShown(row);
+        verify(row.getEntry().getSbn()).getLogMaker();  // This writes most of the log data
+        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
+                MetricsProto.MetricsEvent.TYPE_ACTION));
+    }
+
+    @Test
+    public void testOnMenuClickedLogging() {
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
+        when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
+                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
+
+        ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
+                ArgumentCaptor.forClass(OnMenuEventListener.class);
+
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
+                onMenuEventListenerArgumentCaptor.capture());
+
+        OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
+
+        onMenuEventListener.onMenuClicked(row, 0, 0, mock(
+                NotificationMenuRowPlugin.MenuItem.class));
+        verify(row.getEntry().getSbn()).getLogMaker();  // This writes most of the log data
+        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
+                MetricsProto.MetricsEvent.TYPE_ACTION));
+    }
+
+    @Test
+    public void testDismissListener() {
+        ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
+                dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
+                NotificationStackScrollLayout.DismissListener.class);
+
+        mController.attach(mNotificationStackScrollLayout);
+
+        verify(mNotificationStackScrollLayout).setDismissListener(
+                dismissListenerArgumentCaptor.capture());
+        NotificationStackScrollLayout.DismissListener dismissListener =
+                dismissListenerArgumentCaptor.getValue();
+
+        dismissListener.onDismiss(ROWS_ALL);
+        verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
+    }
+
+    @Test
+    public void testForegroundDismissEnabled() {
+        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
+                mForegroundServiceDungeonView);
+    }
+
+    @Test
+    public void testForegroundDismissaDisabled() {
+        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
+        mController.attach(mNotificationStackScrollLayout);
+        verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
+                any(ForegroundServiceDungeonView.class));
+    }
+
+    private LogMaker logMatcher(int category, int type) {
+        return argThat(new LogMatcher(category, type));
+    }
+
+    private void setupShowEmptyShadeViewState(
+            StatusBarStateController.StateListener statusBarStateListener,
+            boolean toShow) {
+        if (toShow) {
+            statusBarStateListener.onStateChanged(SHADE);
+            mController.setQsExpanded(false);
+            mController.getView().removeAllViews();
+        } else {
+            statusBarStateListener.onStateChanged(KEYGUARD);
+            mController.setQsExpanded(true);
+            mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
+        }
+    }
+
+    static class LogMatcher implements ArgumentMatcher<LogMaker> {
+        private int mCategory, mType;
+
+        LogMatcher(int category, int type) {
+            mCategory = category;
+            mType = type;
+        }
+        public boolean matches(LogMaker l) {
+            return (l.getCategory() == mCategory)
+                    && (l.getType() == mType);
+        }
+
+        public String toString() {
+            return String.format("LogMaker(%d, %d)", mCategory, mType);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b03df880..6185437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -52,7 +52,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -63,7 +62,6 @@
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -101,14 +99,12 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
-    @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
     @Mock private KeyguardBypassController mBypassController;
     @Mock private NotificationSectionsManager mNotificationSectionsManager;
     @Mock private NotificationSection mNotificationSection;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
     @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
-    @Mock private FeatureFlags mFeatureFlags;
     @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     @Before
@@ -148,10 +144,8 @@
                 mGroupMembershipManger,
                 mGroupExpansionManager,
                 mAmbientState,
-                mFeatureFlags,
                 mUnlockedScreenOffAnimationController);
-        mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
-                mNotificationSwipeHelper);
+        mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelfController(notificationShelfController);
         mStackScroller.setStatusBar(mBar);
@@ -233,21 +227,24 @@
     @Test
     @UiThreadTest
     public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
         final int[] expectedStackHeight = {0};
 
         mStackScroller.addOnExpandedHeightChangedListener((expandedHeight, appear) -> {
             assertWithMessage("Given shade enabled: %s",
-                    mFeatureFlags.isTwoColumnNotificationShadeEnabled())
+                    true)
                     .that(mStackScroller.getHeight())
                     .isEqualTo(expectedStackHeight[0]);
         });
 
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
         expectedStackHeight[0] = 0;
         mStackScroller.setExpandedHeight(100f);
 
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
         expectedStackHeight[0] = 100;
         mStackScroller.setExpandedHeight(100f);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
deleted file mode 100644
index f376e88..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ /dev/null
@@ -1,413 +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.statusbar.notification.stack;
-
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-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.content.res.Resources;
-import android.metrics.LogMaker;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.media.KeyguardMediaController;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.tuner.TunerService;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link NotificationStackScrollLayoutController}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class NotificationStackScrollerControllerTest extends SysuiTestCase {
-
-    @Mock private NotificationGutsManager mNotificationGutsManager;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
-    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
-    @Mock private TunerService mTunerService;
-    @Mock private DynamicPrivacyController mDynamicPrivacyController;
-    @Mock private ConfigurationController mConfigurationController;
-    @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock private ZenModeController mZenModeController;
-    @Mock private KeyguardMediaController mKeyguardMediaController;
-    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
-    @Mock private KeyguardBypassController mKeyguardBypassController;
-    @Mock private SysuiColorExtractor mColorExtractor;
-    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
-    @Mock private MetricsLogger mMetricsLogger;
-    @Mock private Resources mResources;
-    @Mock(answer = Answers.RETURNS_SELF)
-    private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
-    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
-    @Mock private StatusBar mStatusBar;
-    @Mock private ScrimController mScrimController;
-    @Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
-    @Mock private SectionHeaderController mSilentHeaderController;
-    @Mock private FeatureFlags mFeatureFlags;
-    @Mock private NotifPipeline mNotifPipeline;
-    @Mock private NotifCollection mNotifCollection;
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private IStatusBarService mIStatusBarService;
-    @Mock private UiEventLogger mUiEventLogger;
-    @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
-    @Mock private ForegroundServiceSectionController mFgServicesSectionController;
-    @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
-    @Mock private LayoutInflater mLayoutInflater;
-    @Mock private NotificationRemoteInputManager mRemoteInputManager;
-    @Mock private RemoteInputController mRemoteInputController;
-    @Mock private VisualStabilityManager mVisualStabilityManager;
-    @Mock private ShadeController mShadeController;
-
-    @Captor
-    private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
-
-    private NotificationStackScrollLayoutController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
-        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
-        when(mFgServicesSectionController.createView(mLayoutInflater))
-                .thenReturn(mForegroundServiceDungeonView);
-        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-
-        mController = new NotificationStackScrollLayoutController(
-                true,
-                mNotificationGutsManager,
-                mHeadsUpManager,
-                mNotificationRoundnessManager,
-                mTunerService,
-                mDynamicPrivacyController,
-                mConfigurationController,
-                mSysuiStatusBarStateController,
-                mKeyguardMediaController,
-                mKeyguardBypassController,
-                mZenModeController,
-                mColorExtractor,
-                mNotificationLockscreenUserManager,
-                mMetricsLogger,
-                new FalsingCollectorFake(),
-                new FalsingManagerFake(),
-                mResources,
-                mNotificationSwipeHelperBuilder,
-                mStatusBar,
-                mScrimController,
-                mLegacyGroupManager,
-                mLegacyGroupManager,
-                mSilentHeaderController,
-                mFeatureFlags,
-                mNotifPipeline,
-                mNotifCollection,
-                mEntryManager,
-                mLockscreenShadeTransitionController,
-                mIStatusBarService,
-                mUiEventLogger,
-                mFgFeatureController,
-                mFgServicesSectionController,
-                mLayoutInflater,
-                mRemoteInputManager,
-                mVisualStabilityManager,
-                mShadeController
-        );
-
-        when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
-    }
-
-    @Test
-    public void testAttach_viewAlreadyAttached() {
-        mController.attach(mNotificationStackScrollLayout);
-
-        verify(mConfigurationController).addCallback(
-                any(ConfigurationController.ConfigurationListener.class));
-    }
-    @Test
-    public void testAttach_viewAttachedAfterInit() {
-        when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(false);
-
-        mController.attach(mNotificationStackScrollLayout);
-
-        verify(mConfigurationController, never()).addCallback(
-                any(ConfigurationController.ConfigurationListener.class));
-
-        mController.mOnAttachStateChangeListener.onViewAttachedToWindow(
-                mNotificationStackScrollLayout);
-
-        verify(mConfigurationController).addCallback(
-                any(ConfigurationController.ConfigurationListener.class));
-    }
-
-    @Test
-    public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() {
-        mController.attach(mNotificationStackScrollLayout);
-        mController.mConfigurationListener.onDensityOrFontScaleChanged();
-        verify(mNotificationStackScrollLayout).reinflateViews();
-    }
-
-    @Test
-    public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mSysuiStatusBarStateController).addCallback(
-                mStateListenerArgumentCaptor.capture(), anyInt());
-        StatusBarStateController.StateListener stateListener =
-                mStateListenerArgumentCaptor.getValue();
-
-        setupShowEmptyShadeViewState(stateListener, true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                true /* visible */,
-
-                true /* notifVisibleInShade */);
-
-        setupShowEmptyShadeViewState(stateListener, false);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                false /* visible */,
-                true /* notifVisibleInShade */);
-    }
-
-    @Test
-    public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mSysuiStatusBarStateController).addCallback(
-                mStateListenerArgumentCaptor.capture(), anyInt());
-        StatusBarStateController.StateListener stateListener =
-                mStateListenerArgumentCaptor.getValue();
-
-        setupShowEmptyShadeViewState(stateListener, true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                true /* visible */,
-                false /* notifVisibleInShade */);
-
-        setupShowEmptyShadeViewState(stateListener, false);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                false /* visible */,
-                false /* notifVisibleInShade */);
-    }
-
-    @Test
-    public void testOnUserChange_verifySensitiveProfile() {
-        when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
-
-        ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
-                .forClass(UserChangedListener.class);
-
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationLockscreenUserManager)
-                .addUserChangedListener(userChangedCaptor.capture());
-        reset(mNotificationStackScrollLayout);
-
-        UserChangedListener changedListener = userChangedCaptor.getValue();
-        changedListener.onUserChanged(0);
-        verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
-    }
-
-    @Test
-    public void testOnStatePostChange_verifyIfProfileIsPublic() {
-        when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
-
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mSysuiStatusBarStateController).addCallback(
-                mStateListenerArgumentCaptor.capture(), anyInt());
-
-        StatusBarStateController.StateListener stateListener =
-                mStateListenerArgumentCaptor.getValue();
-
-        stateListener.onStatePostChange();
-        verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
-    }
-
-    @Test
-    public void testOnMenuShownLogging() {
-        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
-        when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
-                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
-        ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
-                ArgumentCaptor.forClass(OnMenuEventListener.class);
-
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
-                onMenuEventListenerArgumentCaptor.capture());
-
-        OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
-
-        onMenuEventListener.onMenuShown(row);
-        verify(row.getEntry().getSbn()).getLogMaker();  // This writes most of the log data
-        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR,
-                MetricsProto.MetricsEvent.TYPE_ACTION));
-    }
-
-    @Test
-    public void testOnMenuClickedLogging() {
-        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
-        when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
-                MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
-        ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
-                ArgumentCaptor.forClass(OnMenuEventListener.class);
-
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
-                onMenuEventListenerArgumentCaptor.capture());
-
-        OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
-
-        onMenuEventListener.onMenuClicked(row, 0, 0, mock(
-                NotificationMenuRowPlugin.MenuItem.class));
-        verify(row.getEntry().getSbn()).getLogMaker();  // This writes most of the log data
-        verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR,
-                MetricsProto.MetricsEvent.TYPE_ACTION));
-    }
-
-    @Test
-    public void testDismissListener() {
-        ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
-                dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
-                NotificationStackScrollLayout.DismissListener.class);
-
-        mController.attach(mNotificationStackScrollLayout);
-
-        verify(mNotificationStackScrollLayout).setDismissListener(
-                dismissListenerArgumentCaptor.capture());
-        NotificationStackScrollLayout.DismissListener dismissListener =
-                dismissListenerArgumentCaptor.getValue();
-
-        dismissListener.onDismiss(ROWS_ALL);
-        verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
-    }
-
-    @Test
-    public void testForegroundDismissEnabled() {
-        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
-                mForegroundServiceDungeonView);
-    }
-
-    @Test
-    public void testForegroundDismissaDisabled() {
-        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
-                any(ForegroundServiceDungeonView.class));
-    }
-
-    private LogMaker logMatcher(int category, int type) {
-        return argThat(new LogMatcher(category, type));
-    }
-
-    private void setupShowEmptyShadeViewState(
-            StatusBarStateController.StateListener statusBarStateListener,
-            boolean toShow) {
-        if (toShow) {
-            statusBarStateListener.onStateChanged(SHADE);
-            mController.setQsExpanded(false);
-            mController.getView().removeAllViews();
-        } else {
-            statusBarStateListener.onStateChanged(KEYGUARD);
-            mController.setQsExpanded(true);
-            mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
-        }
-    }
-
-    static class LogMatcher implements ArgumentMatcher<LogMaker> {
-        private int mCategory, mType;
-
-        LogMatcher(int category, int type) {
-            mCategory = category;
-            mType = type;
-        }
-        public boolean matches(LogMaker l) {
-            return (l.getCategory() == mCategory)
-                    && (l.getType() == mType);
-        }
-
-        public String toString() {
-            return String.format("LogMaker(%d, %d)", mCategory, mType);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index bc53074..f10565c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -38,8 +38,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -51,6 +51,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.util.Optional;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -61,6 +63,7 @@
     private OngoingCallController mOngoingCallController;
     private SystemStatusAnimationScheduler mAnimationScheduler;
     private StatusBarLocationPublisher mLocationPublisher;
+
     // Set in instantiate()
     private StatusBarIconController mStatusBarIconController;
     private NetworkController mNetworkController;
@@ -70,12 +73,15 @@
     private final StatusBar mStatusBar = mock(StatusBar.class);
     private final CommandQueue mCommandQueue = mock(CommandQueue.class);
 
+
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
     }
 
     @Before
     public void setup() {
+        mStatusBarStateController = mDependency
+                .injectMockDependency(StatusBarStateController.class);
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         when(mStatusBar.getPanelController()).thenReturn(
                 mock(NotificationPanelViewController.class));
@@ -248,10 +254,11 @@
                 mKeyguardStateController,
                 mNetworkController,
                 mStatusBarStateController,
-                mStatusBar,
+                () -> Optional.of(mStatusBar),
                 mCommandQueue);
     }
 
+
     private void setUpNotificationIconAreaController() {
         mMockNotificationAreaController = mock(NotificationIconAreaController.class);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index cdfab1e..c3adee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -102,7 +102,8 @@
                 null /* appearanceRegions */,
                 false /* navbarColorManagedByIme */,
                 BEHAVIOR_DEFAULT,
-                false /* isFullscreen */);
+                null /* requestedState */,
+                null /* packageName */);
         assertTrue(mLightsOutNotifController.areLightsOut());
     }
 
@@ -114,7 +115,8 @@
                 null /* appearanceRegions */,
                 false /* navbarColorManagedByIme */,
                 BEHAVIOR_DEFAULT,
-                false /* isFullscreen */);
+                null /* requestedState */,
+                null /* packageName */);
         assertFalse(mLightsOutNotifController.areLightsOut());
     }
 
@@ -144,7 +146,8 @@
                 null /* appearanceRegions */,
                 false /* navbarColorManagedByIme */,
                 BEHAVIOR_DEFAULT,
-                false /* isFullscreen */);
+                null /* requestedState */,
+                null /* packageName */);
 
         // THEN we should show dot
         assertTrue(mLightsOutNotifController.shouldShowDot());
@@ -163,7 +166,8 @@
                 null /* appearanceRegions */,
                 false /* navbarColorManagedByIme */,
                 BEHAVIOR_DEFAULT,
-                false /* isFullscreen */);
+                null /* requestedState */,
+                null /* packageName */);
 
         // THEN we shouldn't show the dot
         assertFalse(mLightsOutNotifController.shouldShowDot());
@@ -182,7 +186,8 @@
                 null /* appearanceRegions */,
                 false /* navbarColorManagedByIme */,
                 BEHAVIOR_DEFAULT,
-                false /* isFullscreen */);
+                null /* requestedState */,
+                null /* packageName */);
 
         // THEN we shouldn't show the dot
         assertFalse(mLightsOutNotifController.shouldShowDot());
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 f8fc224..a8a1b8b 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
@@ -18,6 +18,8 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
@@ -35,6 +37,7 @@
 import static org.mockito.Mockito.doAnswer;
 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;
 
@@ -96,7 +99,6 @@
 import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -263,8 +265,6 @@
     @Mock
     private MediaDataManager mMediaDataManager;
     @Mock
-    private FeatureFlags mFeatureFlags;
-    @Mock
     private AmbientState mAmbientState;
     @Mock
     private UserManager mUserManager;
@@ -281,6 +281,8 @@
     @Mock
     private SecureSettings mSecureSettings;
     @Mock
+    private SplitShadeHeaderController mSplitShadeHeaderController;
+    @Mock
     private ContentResolver mContentResolver;
     @Mock
     private TapAgainViewController mTapAgainViewController;
@@ -351,6 +353,7 @@
         mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
         mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
         mNotificationContainerParent.addView(mKeyguardStatusView);
+        mNotificationContainerParent.onFinishInflate();
         when(mView.findViewById(R.id.notification_container_parent))
                 .thenReturn(mNotificationContainerParent);
         when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
@@ -428,7 +431,6 @@
                 mNotificationShadeDepthController,
                 mAmbientState,
                 mLockIconViewController,
-                mFeatureFlags,
                 mKeyguardMediaController,
                 mPrivacyDotViewController,
                 mTapAgainViewController,
@@ -439,6 +441,7 @@
                 mRecordingController,
                 new FakeExecutor(new FakeSystemClock()),
                 mSecureSettings,
+                mSplitShadeHeaderController,
                 mUnlockedScreenOffAnimationController,
                 mNotificationRemoteInputManager);
         mNotificationPanelViewController.initDependencies(
@@ -553,7 +556,7 @@
 
     @Test
     public void testAllChildrenOfNotificationContainer_haveIds() {
-        enableSplitShade();
+        enableSplitShade(/* enabled= */ true);
         mNotificationContainerParent.removeAllViews();
         mNotificationContainerParent.addView(newViewWithId(1));
         mNotificationContainerParent.addView(newViewWithId(View.NO_ID));
@@ -566,7 +569,7 @@
 
     @Test
     public void testSinglePaneShadeLayout_isAlignedToParent() {
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+        enableSplitShade(/* enabled= */ false);
 
         mNotificationPanelViewController.updateResources();
 
@@ -577,17 +580,19 @@
     }
 
     @Test
-    public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() {
+    public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
         mNotificationPanelViewController.updateResources();
-
-        assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
-                .isEqualTo(ConstraintSet.PARENT_ID);
-
-        enableSplitShade();
-        mNotificationPanelViewController.updateResources();
-
         assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
                 .isEqualTo(R.id.qs_edge_guideline);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        mNotificationPanelViewController.updateResources();
+        assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
+                .isEqualTo(ConstraintSet.PARENT_ID);
     }
 
     @Test
@@ -624,7 +629,7 @@
 
     @Test
     public void testSplitShadeLayout_isAlignedToGuideline() {
-        enableSplitShade();
+        enableSplitShade(/* enabled= */ true);
 
         mNotificationPanelViewController.updateResources();
 
@@ -636,7 +641,7 @@
 
     @Test
     public void testSinglePaneShadeLayout_childrenHaveConstantWidth() {
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+        enableSplitShade(/* enabled= */ false);
 
         mNotificationPanelViewController.updateResources();
 
@@ -648,7 +653,7 @@
 
     @Test
     public void testSplitShadeLayout_childrenHaveZeroWidth() {
-        enableSplitShade();
+        enableSplitShade(/* enabled= */ true);
 
         mNotificationPanelViewController.updateResources();
 
@@ -660,7 +665,7 @@
     public void testOnDragDownEvent_horizontalTranslationIsZeroForSplitShade() {
         when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f);
         when(mView.getWidth()).thenReturn(800);
-        enableSplitShade();
+        enableSplitShade(/* enabled= */ true);
 
         onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN,
                 200f /* x position */, 0f, 0));
@@ -694,7 +699,7 @@
     @Test
     public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
         mStatusBarStateController.setState(SHADE);
-        enableSplitShade();
+        enableSplitShade(/* enabled= */ true);
         mNotificationPanelViewController.setQsExpanded(true);
 
         assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
@@ -748,6 +753,56 @@
         verify(mTapAgainViewController).show();
     }
 
+    @Test
+    public void testSwitchesToCorrectClockInSinglePaneShade() {
+        mStatusBarStateController.setState(KEYGUARD);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        triggerPositionClockAndNotifications();
+        verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        mNotificationPanelViewController.closeQs();
+        verify(mKeyguardStatusViewController).displayClock(SMALL);
+    }
+
+    @Test
+    public void testSwitchesToCorrectClockInSplitShade() {
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        triggerPositionClockAndNotifications();
+        verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        triggerPositionClockAndNotifications();
+        verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
+        verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+    }
+
+    @Test
+    public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
+        mStatusBarStateController.setState(KEYGUARD);
+        enableSplitShade(/* enabled= */ true);
+        when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+
+        // one notification + media player visible
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        triggerPositionClockAndNotifications();
+        verify(mKeyguardStatusViewController).displayClock(SMALL);
+
+        // only media player visible
+        when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        triggerPositionClockAndNotifications();
+        verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL);
+        verify(mKeyguardStatusViewController, never()).displayClock(LARGE);
+    }
+
+    private void triggerPositionClockAndNotifications() {
+        mNotificationPanelViewController.closeQs();
+    }
+
     private FalsingManager.FalsingTapListener getFalsingTapListener() {
         for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
             listener.onViewAttachedToWindow(mView);
@@ -778,9 +833,8 @@
         return constraintSet.getConstraint(id).layout;
     }
 
-    private void enableSplitShade() {
-        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
-        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+    private void enableSplitShade(boolean enabled) {
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
         mNotificationPanelViewController.updateResources();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 678b193..2685b76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -576,6 +576,49 @@
     }
 
     @Test
+    public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+        mScrimController.setClipsQsScrim(true);
+        mScrimController.transitionTo(ScrimState.BOUNCER);
+
+        mScrimController.setClipsQsScrim(false);
+
+        finishAnimationsImmediately();
+        // Front scrim should be transparent
+        // Back scrim should be visible without tint
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mNotificationsScrim, TRANSPARENT,
+                mScrimBehind, OPAQUE));
+        assertScrimTinted(Map.of(
+                mScrimInFront, false,
+                mScrimBehind, false,
+                mNotificationsScrim, false
+        ));
+    }
+
+    @Test
+    public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+        mScrimController.setClipsQsScrim(false);
+        mScrimController.transitionTo(ScrimState.BOUNCER);
+
+        mScrimController.setClipsQsScrim(true);
+
+        finishAnimationsImmediately();
+        // Front scrim should be transparent
+        // Back scrim should be clipping QS
+        // Notif scrim should be visible without tint
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mNotificationsScrim, OPAQUE,
+                mScrimBehind, OPAQUE));
+        assertScrimTinted(Map.of(
+                mScrimInFront, false,
+                mScrimBehind, true,
+                mNotificationsScrim, false
+        ));
+    }
+
+    @Test
     public void transitionToBouncer() {
         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
         finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 4796cd7..10eb71f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -47,7 +47,8 @@
     @Test
     fun testGetBoundingRectForPrivacyChipForRotation_noCutout() {
         val screenBounds = Rect(0, 0, 1080, 2160)
-        val roundedCornerPadding = 20
+        val minLeftPadding = 20
+        val minRightPadding = 20
         val sbHeightPortrait = 100
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
@@ -64,7 +65,8 @@
                 null,
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
         /* 1080 - 20 (rounded corner) - 30 (chip),
@@ -92,7 +94,8 @@
                 dc,
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
         /* 2160 - 20 (rounded corner) - 30 (chip),
@@ -118,7 +121,8 @@
         // GIVEN a device in portrait mode with width < height and a display cutout in the top-left
         val screenBounds = Rect(0, 0, 1080, 2160)
         val dcBounds = Rect(0, 0, 100, 100)
-        val roundedCornerPadding = 20
+        val minLeftPadding = 20
+        val minRightPadding = 20
         val sbHeightPortrait = 100
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
@@ -131,7 +135,7 @@
         var targetRotation = ROTATION_NONE
         var expectedBounds = Rect(dcBounds.right,
                 0,
-                screenBounds.right - roundedCornerPadding,
+                screenBounds.right - minRightPadding,
                 sbHeightPortrait)
 
         var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -140,14 +144,15 @@
                 dc,
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_LANDSCAPE
         expectedBounds = Rect(dcBounds.height(),
                 0,
-                screenBounds.height() - roundedCornerPadding,
+                screenBounds.height() - minRightPadding,
                 sbHeightLandscape)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -156,16 +161,17 @@
                 dc,
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         // THEN the side that does NOT share a short side with the display cutout ignores the
         // display cutout bounds
         targetRotation = ROTATION_UPSIDE_DOWN
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.width() - roundedCornerPadding,
+                screenBounds.width() - minRightPadding,
                 sbHeightPortrait)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -174,13 +180,14 @@
                 dc,
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         // Phone in portrait, seascape (rot_270) bounds
         targetRotation = ROTATION_SEASCAPE
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
                 screenBounds.height() - dcBounds.height(),
                 sbHeightLandscape)
@@ -191,7 +198,8 @@
                 dc,
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
     }
@@ -205,7 +213,8 @@
         val screenBounds = Rect(0, 0, 1080, 2160)
         // cutout centered at the top
         val dcBounds = Rect(490, 0, 590, 100)
-        val roundedCornerPadding = 20
+        val minLeftPadding = 20
+        val minRightPadding = 20
         val sbHeightPortrait = 100
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
@@ -216,9 +225,9 @@
         // THEN only the landscape/seascape rotations should avoid the cutout area because of the
         // potential letterboxing
         var targetRotation = ROTATION_NONE
-        var expectedBounds = Rect(roundedCornerPadding,
+        var expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.right - roundedCornerPadding,
+                screenBounds.right - minRightPadding,
                 sbHeightPortrait)
 
         var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -227,14 +236,15 @@
                 dc,
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_LANDSCAPE
         expectedBounds = Rect(dcBounds.height(),
                 0,
-                screenBounds.height() - roundedCornerPadding,
+                screenBounds.height() - minRightPadding,
                 sbHeightLandscape)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -243,14 +253,15 @@
                 dc,
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_UPSIDE_DOWN
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.right - roundedCornerPadding,
+                screenBounds.right - minRightPadding,
                 sbHeightPortrait)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -259,12 +270,13 @@
                 dc,
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_SEASCAPE
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
                 screenBounds.height() - dcBounds.height(),
                 sbHeightLandscape)
@@ -275,7 +287,8 @@
                 dc,
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
     }
@@ -285,7 +298,8 @@
         // GIVEN device in portrait mode, where width < height and no cutout
         val currentRotation = ROTATION_NONE
         val screenBounds = Rect(0, 0, 1080, 2160)
-        val roundedCornerPadding = 20
+        val minLeftPadding = 20
+        val minRightPadding = 20
         val sbHeightPortrait = 100
         val sbHeightLandscape = 60
 
@@ -293,9 +307,9 @@
 
         // THEN content insets should only use rounded corner padding
         var targetRotation = ROTATION_NONE
-        var expectedBounds = Rect(roundedCornerPadding,
+        var expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.right - roundedCornerPadding,
+                screenBounds.right - minRightPadding,
                 sbHeightPortrait)
 
         var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -304,13 +318,14 @@
                 null, /* no cutout */
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_LANDSCAPE
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.height() - roundedCornerPadding,
+                screenBounds.height() - minRightPadding,
                 sbHeightLandscape)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -319,13 +334,14 @@
                 null, /* no cutout */
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_UPSIDE_DOWN
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.width() - roundedCornerPadding,
+                screenBounds.width() - minRightPadding,
                 sbHeightPortrait)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -334,13 +350,14 @@
                 null, /* no cutout */
                 windowMetrics,
                 sbHeightPortrait,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
         targetRotation = ROTATION_LANDSCAPE
-        expectedBounds = Rect(roundedCornerPadding,
+        expectedBounds = Rect(minLeftPadding,
                 0,
-                screenBounds.height() - roundedCornerPadding,
+                screenBounds.height() - minRightPadding,
                 sbHeightLandscape)
 
         bounds = calculateInsetsForRotationWithRotatedResources(
@@ -349,7 +366,41 @@
                 null, /* no cutout */
                 windowMetrics,
                 sbHeightLandscape,
-                roundedCornerPadding)
+                minLeftPadding,
+                minRightPadding)
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+    }
+
+    @Test
+    fun testMinLeftRight_accountsForDisplayCutout() {
+        // GIVEN a device in portrait mode with width < height and a display cutout in the top-left
+        val screenBounds = Rect(0, 0, 1080, 2160)
+        val dcBounds = Rect(0, 0, 100, 100)
+        val minLeftPadding = 80
+        val minRightPadding = 150
+        val sbHeightPortrait = 100
+        val sbHeightLandscape = 60
+        val currentRotation = ROTATION_NONE
+
+        `when`(windowMetrics.bounds).thenReturn(screenBounds)
+        `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+        // THEN left should be set to the display cutout width, and right should use the minRight
+        var targetRotation = ROTATION_NONE
+        var expectedBounds = Rect(dcBounds.right,
+                0,
+                screenBounds.right - minRightPadding,
+                sbHeightPortrait)
+
+        var bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightPortrait,
+                minLeftPadding,
+                minRightPadding)
+
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
     }
 
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 2c2833a..b351d52 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
@@ -98,6 +98,7 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -146,6 +147,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
@@ -266,6 +268,8 @@
     @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
     @Mock private WiredChargingRippleController mWiredChargingRippleController;
+    @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
+    @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
     @Mock private OngoingCallController mOngoingCallController;
     @Mock private SystemStatusAnimationScheduler mAnimationScheduler;
     @Mock private StatusBarLocationPublisher mLocationPublisher;
@@ -354,7 +358,7 @@
         mShadeController = new ShadeControllerImpl(mCommandQueue,
                 mStatusBarStateController, mNotificationShadeWindowController,
                 mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
-                () -> mStatusBar, () -> mAssistManager, Optional.of(mBubbles));
+                () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles));
 
         mStatusBar = new StatusBar(
                 mContext,
@@ -440,6 +444,8 @@
                 mNotificationIconAreaController,
                 mBrightnessSliderFactory,
                 mWiredChargingRippleController,
+                mUnfoldTransitionConfig,
+                mUnfoldLightRevealOverlayAnimationLazy,
                 mOngoingCallController,
                 mAnimationScheduler,
                 mLocationPublisher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index dd8354d..0d4d889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 
@@ -25,15 +27,19 @@
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ShortcutManager;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.ContentInfo;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -238,4 +244,39 @@
                 RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND.getId(),
                 mUiEventLoggerFake.eventId(1));
     }
+
+    @Test
+    public void testUiEventLogging_openAndAttach() throws Exception {
+        NotificationTestHelper helper = new NotificationTestHelper(
+                mContext,
+                mDependency,
+                TestableLooper.get(this));
+        ExpandableNotificationRow row = helper.createRow();
+        RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+
+        setTestPendingIntent(view);
+
+        // Open view, attach an image
+        view.focus();
+        EditText editText = view.findViewById(R.id.remote_input_text);
+        editText.setText(TEST_REPLY);
+        ClipDescription description = new ClipDescription("", new String[] {"image/png"});
+        // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+        ClipData clip = new ClipData(description, new ClipData.Item(
+                Uri.parse("android.resource://com.android.systemui/"
+                        + R.drawable.default_thumbnail)));
+        ContentInfo payload =
+                new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
+        view.setAttachment(payload);
+        mReceiver.waitForIntent();
+
+        assertEquals(2, mUiEventLoggerFake.numLogs());
+        assertEquals(
+                RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN.getId(),
+                mUiEventLoggerFake.eventId(0));
+        assertEquals(
+                RemoteInputView.NotificationRemoteInputEvent
+                        .NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE.getId(),
+                mUiEventLoggerFake.eventId(1));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
new file mode 100644
index 0000000..c365ef2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.IActivityTaskManager
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.hardware.face.FaceManager
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.UserIcons
+import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.QSUserSwitcherEvent
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.telephony.TelephonyListenerManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UserSwitcherControllerTest : SysuiTestCase() {
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var handler: Handler
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var activityTaskManager: IActivityTaskManager
+    @Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
+    @Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
+    @Mock private lateinit var secureSettings: SecureSettings
+    @Mock private lateinit var falsingManager: FalsingManager
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var uiBgExecutor: FakeExecutor
+    private lateinit var uiEventLogger: UiEventLoggerFake
+    private lateinit var userSwitcherController: UserSwitcherController
+    private lateinit var picture: Bitmap
+    private val ownerId = UserHandle.USER_SYSTEM
+    private val ownerInfo = UserInfo(ownerId, "Owner", null,
+            UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL or UserInfo.FLAG_INITIALIZED or
+                    UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM,
+            UserManager.USER_TYPE_FULL_SYSTEM)
+    private val guestId = 1234
+    private val guestInfo = UserInfo(guestId, "Guest", null,
+            UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        uiBgExecutor = FakeExecutor(FakeSystemClock())
+        uiEventLogger = UiEventLoggerFake()
+
+        context.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_guestUserAutoCreated, false)
+
+        mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
+        mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
+                mock(FingerprintManager::class.java))
+
+        `when`(userManager.canAddMoreUsers()).thenReturn(true)
+
+        userSwitcherController = UserSwitcherController(context,
+                userManager,
+                userTracker,
+                keyguardStateController,
+                handler,
+                activityStarter,
+                broadcastDispatcher,
+                uiEventLogger,
+                falsingManager,
+                telephonyListenerManager,
+                activityTaskManager,
+                userDetailAdapter,
+                secureSettings,
+                uiBgExecutor)
+        userSwitcherController.mPauseRefreshUsers = true
+
+        picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
+    }
+
+    @Test
+    fun testAddGuest_okButtonPressed_isLogged() {
+        val emptyGuestUserRecord = UserSwitcherController.UserRecord(
+                null,
+                null,
+                true /* guest */,
+                false /* current */,
+                false /* isAddUser */,
+                false /* isRestricted */,
+                true /* isSwitchToEnabled */)
+        `when`(userTracker.userId).thenReturn(ownerId)
+        `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
+        `when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
+
+        userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
+        testableLooper.processAllMessages()
+        assertEquals(1, uiEventLogger.numLogs())
+        assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
+    }
+
+    @Test
+    fun testRemoveGuest_removeButtonPressed_isLogged() {
+        val currentGuestUserRecord = UserSwitcherController.UserRecord(
+                guestInfo,
+                picture,
+                true /* guest */,
+                true /* current */,
+                false /* isAddUser */,
+                false /* isRestricted */,
+                true /* isSwitchToEnabled */)
+        `when`(userTracker.userId).thenReturn(guestInfo.id)
+        `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+        userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+        assertNotNull(userSwitcherController.mExitGuestDialog)
+        userSwitcherController.mExitGuestDialog
+                .getButton(DialogInterface.BUTTON_POSITIVE).performClick()
+        testableLooper.processAllMessages()
+        assertEquals(1, uiEventLogger.numLogs())
+        assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
+    }
+
+    @Test
+    fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
+        val currentGuestUserRecord = UserSwitcherController.UserRecord(
+                guestInfo,
+                picture,
+                true /* guest */,
+                true /* current */,
+                false /* isAddUser */,
+                false /* isRestricted */,
+                true /* isSwitchToEnabled */)
+        `when`(userTracker.userId).thenReturn(guestId)
+        `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+        userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+        assertNotNull(userSwitcherController.mExitGuestDialog)
+        userSwitcherController.mExitGuestDialog
+                .getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
+        testableLooper.processAllMessages()
+        assertEquals(0, uiEventLogger.numLogs())
+    }
+
+    @Test
+    fun testWipeGuest_startOverButtonPressed_isLogged() {
+        val currentGuestUserRecord = UserSwitcherController.UserRecord(
+                guestInfo,
+                picture,
+                true /* guest */,
+                false /* current */,
+                false /* isAddUser */,
+                false /* isRestricted */,
+                true /* isSwitchToEnabled */)
+        `when`(userTracker.userId).thenReturn(guestId)
+        `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+        // Simulate that guest user has already logged in
+        `when`(secureSettings.getIntForUser(
+                eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+                .thenReturn(1)
+
+        userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+        // Simulate a user switch event
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+
+        assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+        userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+        assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+        userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+                .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_WIPE).performClick()
+        testableLooper.processAllMessages()
+        assertEquals(1, uiEventLogger.numLogs())
+        assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_WIPE.id, uiEventLogger.eventId(0))
+    }
+
+    @Test
+    fun testWipeGuest_continueButtonPressed_isLogged() {
+        val currentGuestUserRecord = UserSwitcherController.UserRecord(
+                guestInfo,
+                picture,
+                true /* guest */,
+                false /* current */,
+                false /* isAddUser */,
+                false /* isRestricted */,
+                true /* isSwitchToEnabled */)
+        `when`(userTracker.userId).thenReturn(guestId)
+        `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+        // Simulate that guest user has already logged in
+        `when`(secureSettings.getIntForUser(
+                eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+                .thenReturn(1)
+
+        userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+        // Simulate a user switch event
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+
+        assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+        userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+        assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+        userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+                .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_DONTWIPE)
+                .performClick()
+        testableLooper.processAllMessages()
+        assertEquals(1, uiEventLogger.numLogs())
+        assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE.id, uiEventLogger.eventId(0))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
new file mode 100644
index 0000000..871a48c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+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.Mockito.`when`
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Date
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class VariableDateViewControllerTest : SysuiTestCase() {
+
+    companion object {
+        private const val TIME_STAMP = 1_500_000_000_000
+        private const val LONG_PATTERN = "EEEMMMd"
+        private const val SHORT_PATTERN = "MMMd"
+        private const val CHAR_WIDTH = 10f
+    }
+
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var view: VariableDateView
+    @Captor
+    private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
+
+    private var lastText: String? = null
+
+    private lateinit var systemClock: FakeSystemClock
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var testableHandler: Handler
+    private lateinit var controller: VariableDateViewController
+
+    private lateinit var longText: String
+    private lateinit var shortText: String
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        testableHandler = Handler(testableLooper.looper)
+
+        systemClock = FakeSystemClock()
+        systemClock.setCurrentTimeMillis(TIME_STAMP)
+
+        `when`(view.longerPattern).thenReturn(LONG_PATTERN)
+        `when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
+        `when`(view.handler).thenReturn(testableHandler)
+
+        `when`(view.setText(anyString())).thenAnswer {
+            lastText = it.arguments[0] as? String
+            Unit
+        }
+        `when`(view.isAttachedToWindow).thenReturn(true)
+
+        val date = Date(TIME_STAMP)
+        longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
+        shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN))
+
+        // Assume some sizes for the text, the controller doesn't need to know if these sizes are
+        // the true ones
+        `when`(view.getDesiredWidthForText(any())).thenAnswer {
+            getTextLength(it.arguments[0] as CharSequence)
+        }
+
+        controller = VariableDateViewController(
+                systemClock,
+                broadcastDispatcher,
+                testableHandler,
+                view
+        )
+
+        controller.init()
+        testableLooper.processAllMessages()
+
+        verify(view).onAttach(capture(onMeasureListenerCaptor))
+    }
+
+    @Test
+    fun testViewStartsWithLongText() {
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testListenerNotNull() {
+        assertThat(onMeasureListenerCaptor.value).isNotNull()
+    }
+
+    @Test
+    fun testLotsOfSpaceUseLongText() {
+        onMeasureListenerCaptor.value.onMeasureAction(10000)
+
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testSmallSpaceUseEmpty() {
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEmpty()
+    }
+
+    @Test
+    fun testSpaceInBetweenUseShortText() {
+        val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+
+        onMeasureListenerCaptor.value.onMeasureAction(average)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEqualTo(shortText)
+    }
+
+    @Test
+    fun testSwitchBackToLonger() {
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+
+        onMeasureListenerCaptor.value.onMeasureAction(10000)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testNoSwitchingWhenFrozen() {
+        `when`(view.freezeSwitching).thenReturn(true)
+
+        val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+        onMeasureListenerCaptor.value.onMeasureAction(average)
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    private fun getTextLength(text: CharSequence): Float {
+        return text.length * CHAR_WIDTH
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
new file mode 100644
index 0000000..78fc680
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.concurrency;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MessageRouterImplTest extends SysuiTestCase {
+    private static final int MESSAGE_A = 0;
+    private static final int MESSAGE_B = 1;
+    private static final int MESSAGE_C = 2;
+
+    private static final String METADATA_A = "A";
+    private static final String METADATA_B = "B";
+    private static final String METADATA_C = "C";
+    private static final Foobar METADATA_FOO = new Foobar();
+
+    FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    @Mock
+    MessageRouter.SimpleMessageListener mNoMdListener;
+    @Mock
+    MessageRouter.DataMessageListener<String> mStringListener;
+    @Mock
+    MessageRouter.DataMessageListener<Foobar> mFoobarListener;
+    private MessageRouterImpl mMR;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMR = new MessageRouterImpl(mFakeExecutor);
+    }
+
+    @Test
+    public void testSingleMessage_NoMetaData() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+        mMR.sendMessage(MESSAGE_A);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener).onMessage(MESSAGE_A);
+    }
+
+    @Test
+    public void testSingleMessage_WithMetaData() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessage(METADATA_A);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener).onMessage(METADATA_A);
+    }
+
+    @Test
+    public void testMessages_WithMixedMetaData() {
+        mMR.subscribeTo(String.class, mStringListener);
+        mMR.subscribeTo(Foobar.class, mFoobarListener);
+
+        mMR.sendMessage(METADATA_A);
+        verify(mStringListener, never()).onMessage(anyString());
+        verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener).onMessage(METADATA_A);
+        verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+        reset(mStringListener);
+        reset(mFoobarListener);
+
+        mMR.sendMessage(METADATA_FOO);
+        verify(mStringListener, never()).onMessage(anyString());
+        verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(anyString());
+        verify(mFoobarListener).onMessage(METADATA_FOO);
+    }
+
+    @Test
+    public void testMessages_WithAndWithoutMetaData() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessage(MESSAGE_A);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener).onMessage(MESSAGE_A);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        reset(mNoMdListener);
+        reset(mStringListener);
+
+        mMR.sendMessage(METADATA_A);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(anyInt());
+        verify(mStringListener).onMessage(METADATA_A);
+    }
+
+    @Test
+    public void testRepeatedMessage() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_B);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        InOrder ordered = inOrder(mStringListener);
+        mFakeExecutor.runNextReady();
+        ordered.verify(mStringListener).onMessage(METADATA_A);
+        mFakeExecutor.runNextReady();
+        ordered.verify(mStringListener).onMessage(METADATA_A);
+        mFakeExecutor.runNextReady();
+        ordered.verify(mStringListener).onMessage(METADATA_B);
+    }
+
+    @Test
+    public void testCancelMessage() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+        mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+        mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+        mMR.sendMessage(MESSAGE_A);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_C);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mMR.cancelMessages(MESSAGE_B);
+
+        mFakeExecutor.runAllReady();
+
+        InOrder ordered = inOrder(mNoMdListener);
+        ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+        ordered.verify(mNoMdListener).onMessage(MESSAGE_C);
+    }
+
+    @Test
+    public void testSendMessage_NoSubscriber() {
+        mMR.sendMessage(MESSAGE_A);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mMR.sendMessage(MESSAGE_A);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener).onMessage(MESSAGE_A);
+    }
+
+    @Test
+    public void testUnsubscribe_SpecificMessage_NoMetadata() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+        mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+        mMR.sendMessage(MESSAGE_A);
+        mMR.sendMessage(MESSAGE_B);
+
+        mFakeExecutor.runAllReady();
+        InOrder ordered = inOrder(mNoMdListener);
+        ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+        ordered.verify(mNoMdListener).onMessage(MESSAGE_B);
+
+        reset(mNoMdListener);
+        mMR.unsubscribeFrom(MESSAGE_A, mNoMdListener);
+        mMR.sendMessage(MESSAGE_A);
+        mMR.sendMessage(MESSAGE_B);
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+        verify(mNoMdListener).onMessage(MESSAGE_B);
+    }
+
+    @Test
+    public void testUnsubscribe_SpecificMessage_WithMetadata() {
+        mMR.subscribeTo(String.class, mStringListener);
+        mMR.subscribeTo(Foobar.class, mFoobarListener);
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_FOO);
+
+        mFakeExecutor.runNextReady();
+        verify(mStringListener).onMessage(METADATA_A);
+        mFakeExecutor.runNextReady();
+        verify(mFoobarListener).onMessage(METADATA_FOO);
+
+        reset(mStringListener);
+        reset(mFoobarListener);
+        mMR.unsubscribeFrom(String.class, mStringListener);
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_FOO);
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(METADATA_A);
+        verify(mFoobarListener).onMessage(METADATA_FOO);
+    }
+
+    @Test
+    public void testUnsubscribe_AllMessages_NoMetadata() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+        mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+        mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+        mMR.sendMessage(MESSAGE_A);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_C);
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener).onMessage(MESSAGE_A);
+        verify(mNoMdListener).onMessage(MESSAGE_B);
+        verify(mNoMdListener).onMessage(MESSAGE_C);
+
+        reset(mNoMdListener);
+
+        mMR.unsubscribeFrom(mNoMdListener);
+        mMR.sendMessage(MESSAGE_A);
+        mMR.sendMessage(MESSAGE_B);
+        mMR.sendMessage(MESSAGE_C);
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+        verify(mNoMdListener, never()).onMessage(MESSAGE_B);
+        verify(mNoMdListener, never()).onMessage(MESSAGE_C);
+    }
+
+    @Test
+    public void testUnsubscribe_AllMessages_WithMetadata() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_B);
+        mMR.sendMessage(METADATA_C);
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener).onMessage(METADATA_A);
+        verify(mStringListener).onMessage(METADATA_B);
+        verify(mStringListener).onMessage(METADATA_C);
+
+        reset(mStringListener);
+
+        mMR.unsubscribeFrom(mStringListener);
+        mMR.sendMessage(METADATA_A);
+        mMR.sendMessage(METADATA_B);
+        mMR.sendMessage(METADATA_C);
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(METADATA_A);
+        verify(mStringListener, never()).onMessage(METADATA_B);
+        verify(mStringListener, never()).onMessage(METADATA_C);
+    }
+
+    @Test
+    public void testSingleDelayedMessage_NoMetaData() {
+        mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+        mMR.sendMessageDelayed(MESSAGE_A, 100);
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener, never()).onMessage(anyInt());
+
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runAllReady();
+        verify(mNoMdListener).onMessage(MESSAGE_A);
+    }
+
+    @Test
+    public void testSingleDelayedMessage_WithMetaData() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessageDelayed(METADATA_C, 1000);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runAllReady();
+        verify(mStringListener).onMessage(METADATA_C);
+    }
+
+    @Test
+    public void testMultipleDelayedMessages() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessageDelayed(METADATA_A, 100);
+        mMR.sendMessageDelayed(METADATA_B, 1000);
+        mMR.sendMessageDelayed(METADATA_C, 500);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+
+        InOrder ordered = inOrder(mStringListener);
+        ordered.verify(mStringListener).onMessage(METADATA_A);
+        ordered.verify(mStringListener).onMessage(METADATA_C);
+        ordered.verify(mStringListener).onMessage(METADATA_B);
+    }
+
+    @Test
+    public void testCancelDelayedMessages() {
+        mMR.subscribeTo(String.class, mStringListener);
+
+        mMR.sendMessageDelayed(METADATA_A, 100);
+        mMR.sendMessageDelayed(METADATA_B, 1000);
+        mMR.sendMessageDelayed(METADATA_C, 500);
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mFakeExecutor.runAllReady();
+        verify(mStringListener, never()).onMessage(anyString());
+
+        mMR.cancelMessages(String.class);
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
+
+        verify(mStringListener, never()).onMessage(anyString());
+    }
+
+    private static class Foobar {}
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
index bcc20c2..724d14e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
@@ -17,124 +17,94 @@
 package com.android.systemui.util.leak;
 
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
-import android.os.Looper;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
 public class GarbageMonitorTest extends SysuiTestCase {
 
-    private LeakReporter mLeakReporter;
-    private TrackedGarbage mTrackedGarbage;
-    private TestableGarbageMonitor mGarbageMonitor;
+    @Mock private LeakReporter mLeakReporter;
+    @Mock private TrackedGarbage mTrackedGarbage;
+    private GarbageMonitor mGarbageMonitor;
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setup() {
-        mTrackedGarbage = mock(TrackedGarbage.class);
-        mLeakReporter = mock(LeakReporter.class);
+        MockitoAnnotations.initMocks(this);
         mGarbageMonitor =
-                new TestableGarbageMonitor(
+                new GarbageMonitor(
                         mContext,
-                        TestableLooper.get(this).getLooper(),
+                        mFakeExecutor,
+                        new MessageRouterImpl(mFakeExecutor),
                         new LeakDetector(null, mTrackedGarbage, null),
                         mLeakReporter);
     }
 
     @Test
-    public void testCallbacks_getScheduled() {
-        mGarbageMonitor.startLeakMonitor();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
-    }
-
-    @Test
-    public void testNoGarbage_doesntDump() {
-        when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
-
-        mGarbageMonitor.startLeakMonitor();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
-
-        verify(mLeakReporter, never()).dumpLeak(anyInt());
-    }
-
-    @Test
     public void testALittleGarbage_doesntDump() {
-        when(mTrackedGarbage.countOldGarbage()).thenReturn(4);
+        when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE);
 
-        mGarbageMonitor.startLeakMonitor();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
-        mGarbageMonitor.runCallbacksOnce();
+        mGarbageMonitor.reinspectGarbageAfterGc();
 
         verify(mLeakReporter, never()).dumpLeak(anyInt());
     }
 
     @Test
     public void testTransientGarbage_doesntDump() {
-        when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+        when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
 
+        // Start the leak monitor. Nothing gets reported immediately.
         mGarbageMonitor.startLeakMonitor();
-        mGarbageMonitor.runInspectCallback();
+        mFakeExecutor.runAllReady();
+        verify(mLeakReporter, never()).dumpLeak(anyInt());
 
+        // Garbage gets reset to 0 before the leak reporte actually gets called.
         when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
 
-        mGarbageMonitor.runReinspectCallback();
-
+        // Therefore nothing gets dumped.
         verify(mLeakReporter, never()).dumpLeak(anyInt());
     }
 
     @Test
     public void testLotsOfPersistentGarbage_dumps() {
-        when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+        when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
 
-        mGarbageMonitor.startLeakMonitor();
-        mGarbageMonitor.runCallbacksOnce();
+        mGarbageMonitor.reinspectGarbageAfterGc();
 
-        verify(mLeakReporter).dumpLeak(anyInt());
+        verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
     }
 
-    private static class TestableGarbageMonitor extends GarbageMonitor {
-        public TestableGarbageMonitor(
-                Context context,
-                Looper looper,
-                LeakDetector leakDetector,
-                LeakReporter leakReporter) {
-            super(context, looper, leakDetector, leakReporter);
-        }
+    @Test
+    public void testLotsOfPersistentGarbage_dumpsAfterAtime() {
+        when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
 
-        void runInspectCallback() {
-            startLeakMonitor();
-        }
+        // Start the leak monitor. Nothing gets reported immediately.
+        mGarbageMonitor.startLeakMonitor();
+        mFakeExecutor.runAllReady();
+        verify(mLeakReporter, never()).dumpLeak(anyInt());
 
-        void runReinspectCallback() {
-            reinspectGarbageAfterGc();
-        }
+        mFakeExecutor.advanceClockToLast();
+        mFakeExecutor.runAllReady();
 
-        void runCallbacksOnce() {
-            // Note that TestableLooper doesn't currently support delayed messages so we need to run
-            // callbacks explicitly.
-            runInspectCallback();
-            runReinspectCallback();
-        }
+        verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index f243077..a0b93ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -24,6 +24,7 @@
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -120,6 +121,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import com.google.common.collect.ImmutableList;
@@ -331,7 +333,8 @@
                 mPositioner,
                 mock(DisplayController.class),
                 syncExecutor,
-                mock(Handler.class));
+                mock(Handler.class),
+                mock(SyncTransactionQueue.class));
         mBubbleController.setExpandListener(mBubbleExpandListener);
         spyOn(mBubbleController);
 
@@ -439,7 +442,7 @@
                 mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
 
         mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+                mRow.getKey(), DISMISS_NOTIF_CANCEL);
         verify(mNotificationEntryManager, times(1)).performRemoveNotification(
                 eq(mRow.getSbn()), any(), anyInt());
         assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -1144,6 +1147,28 @@
         // Verify these are in the overflow
         assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
         assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
+
+        // Would have loaded bubbles twice because of user switch
+        verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
+    }
+
+    /**
+     * Verifies we only load the overflow data once.
+     */
+    @Test
+    public void testOverflowLoadedOnce() {
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse();
+
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+        mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL);
+        mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL);
+        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+        verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
     }
 
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index e4c7800..b0dd73a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.wmshell;
 
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -47,6 +48,7 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.UserHandle;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
@@ -100,6 +102,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 
 import org.junit.Before;
@@ -171,6 +174,10 @@
     private ExpandableNotificationRow mNonBubbleNotifRow;
     private BubbleEntry mBubbleEntry;
     private BubbleEntry mBubbleEntry2;
+
+    private BubbleEntry mBubbleEntryUser11;
+    private BubbleEntry mBubbleEntry2User11;
+
     @Mock
     private Bubbles.BubbleExpandListener mBubbleExpandListener;
     @Mock
@@ -240,6 +247,13 @@
         mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
         mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
 
+        UserHandle handle = mock(UserHandle.class);
+        when(handle.getIdentifier()).thenReturn(11);
+        mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry(
+                mNotificationTestHelper.createBubble(handle));
+        mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
+                mNotificationTestHelper.createBubble(handle));
+
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
@@ -275,7 +289,8 @@
                 mPositioner,
                 mock(DisplayController.class),
                 syncExecutor,
-                mock(Handler.class));
+                mock(Handler.class),
+                mock(SyncTransactionQueue.class));
         mBubbleController.setExpandListener(mBubbleExpandListener);
         spyOn(mBubbleController);
 
@@ -906,6 +921,65 @@
                 groupSummary.getEntry().getSbn().getGroupKey()));
     }
 
+
+    /**
+     * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
+     * test the loading from the repository which would be a nice thing to add.
+     */
+    @Test
+    public void testOnUserChanged_overflowState() {
+        int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier();
+        int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier();
+
+        mBubbleController.updateBubble(mBubbleEntry);
+        mBubbleController.updateBubble(mBubbleEntry2);
+        assertTrue(mBubbleController.hasBubbles());
+        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+
+        // Verify these are in the overflow
+        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey())).isNotNull();
+        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
+
+        // Switch users
+        mBubbleController.onUserChanged(secondUserId);
+        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+        // Give this user some bubbles
+        mBubbleController.updateBubble(mBubbleEntryUser11);
+        mBubbleController.updateBubble(mBubbleEntry2User11);
+        assertTrue(mBubbleController.hasBubbles());
+        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+
+        // Verify these are in the overflow
+        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
+        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
+
+        // Would have loaded bubbles twice because of user switch
+        verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
+    }
+
+    /**
+     * Verifies we only load the overflow data once.
+     */
+    @Test
+    public void testOverflowLoadedOnce() {
+        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
+                .thenReturn(mRow);
+        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
+                .thenReturn(mRow2);
+
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+
+        mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
+        mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
+        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+        verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
+    }
+
     /**
      * Sets the bubble metadata flags for this entry. These flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index cd5aa9a..7b77cb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -32,6 +32,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 
 /**
@@ -54,11 +55,12 @@
             BubblePositioner positioner,
             DisplayController displayController,
             ShellExecutor shellMainExecutor,
-            Handler shellMainHandler) {
+            Handler shellMainHandler,
+            SyncTransactionQueue syncQueue) {
         super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
                 statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                 bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
-                shellMainExecutor, shellMainHandler);
+                shellMainExecutor, shellMainHandler, syncQueue);
         setInflateSynchronously(true);
         initialize();
     }
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 5691660..8480702 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@
 import com.android.wm.shell.onehanded.OneHandedEventCallback;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +70,7 @@
     @Mock SysUiState mSysUiState;
     @Mock Pip mPip;
     @Mock LegacySplitScreen mLegacySplitScreen;
+    @Mock SplitScreen mSplitScreen;
     @Mock OneHanded mOneHanded;
     @Mock HideDisplayCutout mHideDisplayCutout;
     @Mock WakefulnessLifecycle mWakefulnessLifecycle;
@@ -81,7 +83,7 @@
         MockitoAnnotations.initMocks(this);
 
         mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
-                Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
+                Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
                 Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
                 mKeyguardUpdateMonitor, mNavigationModeController,
                 mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
@@ -96,8 +98,15 @@
     }
 
     @Test
+    public void initLegacySplitScreen_registersCallbacks() {
+        mWMShell.initLegacySplitScreen(mLegacySplitScreen);
+
+        verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+    }
+
+    @Test
     public void initSplitScreen_registersCallbacks() {
-        mWMShell.initSplitScreen(mLegacySplitScreen);
+        mWMShell.initSplitScreen(mSplitScreen);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
     }
diff --git a/packages/VpnDialogs/res/values-af/strings.xml b/packages/VpnDialogs/res/values-af/strings.xml
index ac82b0e..88ccbd9 100644
--- a/packages/VpnDialogs/res/values-af/strings.xml
+++ b/packages/VpnDialogs/res/values-af/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Verbindingversoek"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit sal toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn boaan jou skerm as VPN aktief is."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn op jou skerm wanneer VPN aktief is."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN is gekoppel"</string>
     <string name="session" msgid="6470628549473641030">"Sessie:"</string>
     <string name="duration" msgid="3584782459928719435">"Tydsduur:"</string>
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index ad9773b..9fc5ff4 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"የግንኙነት ጥያቄ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ መከታተል የሚያስችል የVPN ግንኑነት ማዋቀር ይፈልጋል። ምንጩን የሚያምኑት ብቻ ከሆኑ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ገቢር ሲሆን በማያ ገጽዎ ላይኛው ክፍል ላይ ይታያል።"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ ትራፊክን ለመቆጣጠር የሚያስችል የVPN ግንኙነትን ማዋቀር ይፈልጋል። ምንጩን የሚያምኑ ከሆነ ብቻ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ማያ ገጹ ላይ VPN ገቢር ሲሆን ይታያል።"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ተያይዟል"</string>
     <string name="session" msgid="6470628549473641030">"ክፍለ ጊዜ፡"</string>
     <string name="duration" msgid="3584782459928719435">"ጊዜ"</string>
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 808cde9..33be6a3 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
     <string name="warning" msgid="809658604548412033">"‏يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‏يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة افتراضية خاصة (VPN) يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر على شاشتك عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
     <string name="legacy_title" msgid="192936250066580964">"‏VPN متصلة"</string>
     <string name="session" msgid="6470628549473641030">"الجلسة"</string>
     <string name="duration" msgid="3584782459928719435">"المدة:"</string>
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 45d8458..4669a69 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দৃশ্যমান হয়৷"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>এ এটা ভিপিএন সংযোগ ছেট আপ কৰিব বিচাৰে, যিটোৱে ইয়াক নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ দিয়ে। আপুনি উৎসটোক বিশ্বাস কৰিলেহে গ্ৰহণ কৰক। ভিপিএনটো সক্ৰিয় হৈ থকাৰ সময়ত আপোনাৰ স্ক্ৰীনত&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; প্ৰদৰ্শিত হয়।"</string>
     <string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
     <string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
     <string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
diff --git a/packages/VpnDialogs/res/values-az/strings.xml b/packages/VpnDialogs/res/values-az/strings.xml
index 2bdf23e..d878835 100644
--- a/packages/VpnDialogs/res/values-az/strings.xml
+++ b/packages/VpnDialogs/res/values-az/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Bağlantı Sorğusu"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bağlantı yaratmaq istəyir ki, bu da şəbəkə trafikini izləyə bilər. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. VPN aktiv olan zaman &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ekranın yuxarısında görünür."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> şəbəkə trafikini izləməyə imkan verən VPN bağlantısı yaratmaq istəyir. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN aktiv olan zaman ekranda görünür."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN qoşuludur"</string>
     <string name="session" msgid="6470628549473641030">"Sessiya:"</string>
     <string name="duration" msgid="3584782459928719435">"Müddət:"</string>
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index f40e406..a1075d2 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje na ekranu kada je VPN aktivan."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
     <string name="session" msgid="6470628549473641030">"Sesija:"</string>
     <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 0903c8e..fc3f878 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з\'явіцца значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
     <string name="session" msgid="6470628549473641030">"Сессія"</string>
     <string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-bg/strings.xml b/packages/VpnDialogs/res/values-bg/strings.xml
index 9ac853d..6345f1d 100644
--- a/packages/VpnDialogs/res/values-bg/strings.xml
+++ b/packages/VpnDialogs/res/values-bg/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Заявка за свързване"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с виртуална частна мрежа (VPN), за да може да наблюдава мрежовия трафик. Приемете само ако източникът е надежден. Иконата &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва в долната част на екрана при активирана VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с VPN, за да може да наблюдава трафика в мрежата. Приемете само ако източникът е надежден. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва на екрана при активирана VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN е свързана"</string>
     <string name="session" msgid="6470628549473641030">"Сесия:"</string>
     <string name="duration" msgid="3584782459928719435">"Продължителност:"</string>
diff --git a/packages/VpnDialogs/res/values-bn/strings.xml b/packages/VpnDialogs/res/values-bn/strings.xml
index 5e11fd9..352b786 100644
--- a/packages/VpnDialogs/res/values-bn/strings.xml
+++ b/packages/VpnDialogs/res/values-bn/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"সংযোগের অনুরোধ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট-আপ করতে চাচ্ছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি যদি উৎসটিকে বিশ্বাস করেন, তাহলেই কেবল এতে সম্মতি দিন। VPN সক্রিয় থাকলে আপনার স্ক্রীনের উপরে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দেখা যাবে।"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট আপ করতে চাইছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি সোর্সটি বিশ্বাস করলে একমাত্র তখনই অ্যাক্সেপ্ট করুন। PN অ্যাক্টিভ থাকলে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; আপনার স্ক্রিনে দেখা যায়।"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN সংযুক্ত হয়েছে"</string>
     <string name="session" msgid="6470628549473641030">"অধিবেশন:"</string>
     <string name="duration" msgid="3584782459928719435">"সময়কাল:"</string>
diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml
index 56812d5..fa5f4ea 100644
--- a/packages/VpnDialogs/res/values-bs/strings.xml
+++ b/packages/VpnDialogs/res/values-bs/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
     <string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi podesiti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako je izvor pouzdan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se pojavi na vrhu ekrana kada je VPN aktivna."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako vjerujete izvoru. Kada je VPN aktivan, na ekranu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN veza uspostavljena"</string>
     <string name="session" msgid="6470628549473641030">"Sesija:"</string>
     <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-ca/strings.xml b/packages/VpnDialogs/res/values-ca/strings.xml
index 97738c3..cdb7547 100644
--- a/packages/VpnDialogs/res/values-ca/strings.xml
+++ b/packages/VpnDialogs/res/values-ca/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Sol·licitud de connexió"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti controlar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; es mostra a la part superior de la pantalla quan la VPN està activada."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti monitorar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; és la icona que veuràs a la pantalla quan la VPN estigui activa."</string>
     <string name="legacy_title" msgid="192936250066580964">"La VPN està connectada"</string>
     <string name="session" msgid="6470628549473641030">"Sessió:"</string>
     <string name="duration" msgid="3584782459928719435">"Durada:"</string>
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 5cc809c..c06f6ff 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Žádost o připojení"</string>
     <string name="warning" msgid="809658604548412033">"Aplikace <xliff:g id="APP">%s</xliff:g> žádá o nastavení připojení VPN, pomocí kterého bude moci sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, v horní části obrazovky se zobrazuje tato ikona."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikace <xliff:g id="APP">%s</xliff:g> chce nastavit připojení VPN, které umožňuje sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, na obrazovce se zobrazuje tato ikona."</string>
     <string name="legacy_title" msgid="192936250066580964">"Síť VPN je připojena"</string>
     <string name="session" msgid="6470628549473641030">"Relace:"</string>
     <string name="duration" msgid="3584782459928719435">"Doba trvání:"</string>
diff --git a/packages/VpnDialogs/res/values-da/strings.xml b/packages/VpnDialogs/res/values-da/strings.xml
index 7641158..a4ddc19 100644
--- a/packages/VpnDialogs/res/values-da/strings.xml
+++ b/packages/VpnDialogs/res/values-da/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Forbindelsesanmodning"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-forbindelse, der giver appen mulighed for at registrere netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på din skærm, når VPN-forbindelsen er aktiv."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> anmoder om at konfigurere en VPN-forbindelse, der giver appen tilladelse til at holde øje med netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til appen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på din skærm, når VPN-forbindelsen er aktiv."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN er tilsluttet"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Varighed:"</string>
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index 0f1e009..f38e395 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; angezeigt."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wird auf dem Display angezeigt, wenn VPN aktiv ist."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
     <string name="session" msgid="6470628549473641030">"Sitzung:"</string>
     <string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-el/strings.xml b/packages/VpnDialogs/res/values-el/strings.xml
index 78bcc43..e3eb460 100644
--- a/packages/VpnDialogs/res/values-el/strings.xml
+++ b/packages/VpnDialogs/res/values-el/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Αίτημα σύνδεσης"</string>
     <string name="warning" msgid="809658604548412033">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στο επάνω μέρος της οθόνης σας όταν είναι ενεργό το VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στην οθόνη σας όταν είναι ενεργό το VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"Το VPN συνδέθηκε"</string>
     <string name="session" msgid="6470628549473641030">"Περίοδος σύνδεσης"</string>
     <string name="duration" msgid="3584782459928719435">"Διάρκεια:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rAU/strings.xml b/packages/VpnDialogs/res/values-en-rAU/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rAU/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rAU/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Connection request"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rCA/strings.xml b/packages/VpnDialogs/res/values-en-rCA/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rCA/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Connection request"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rGB/strings.xml b/packages/VpnDialogs/res/values-en-rGB/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rGB/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rGB/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Connection request"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rIN/strings.xml b/packages/VpnDialogs/res/values-en-rIN/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rIN/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rIN/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Connection request"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rXC/strings.xml b/packages/VpnDialogs/res/values-en-rXC/strings.xml
index 9d010e6..f5e2deb 100644
--- a/packages/VpnDialogs/res/values-en-rXC/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rXC/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Connection request‎‏‎‎‏‎"</string>
     <string name="warning" msgid="809658604548412033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active.‎‏‎‎‏‎"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active.‎‏‎‎‏‎"</string>
     <string name="legacy_title" msgid="192936250066580964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎VPN is connected‎‏‎‎‏‎"</string>
     <string name="session" msgid="6470628549473641030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎Session:‎‏‎‎‏‎"</string>
     <string name="duration" msgid="3584782459928719435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎Duration:‎‏‎‎‏‎"</string>
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 21cfc04..108a24e 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita supervisar el tráfico de red. Solo acéptala si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en tu pantalla cuando se active la VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
     <string name="session" msgid="6470628549473641030">"Sesión:"</string>
     <string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 372147f..0eaf359 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en la pantalla cuando la VPN esté activa."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string>
     <string name="session" msgid="6470628549473641030">"Sesión:"</string>
     <string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-et/strings.xml b/packages/VpnDialogs/res/values-et/strings.xml
index c328cd7..140c183 100644
--- a/packages/VpnDialogs/res/values-et/strings.xml
+++ b/packages/VpnDialogs/res/values-et/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Ühendamise taotlus"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraani ülaservas, kui VPN on aktiivne."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraanil, kui VPN on aktiivne."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN on ühendatud"</string>
     <string name="session" msgid="6470628549473641030">"Seansid"</string>
     <string name="duration" msgid="3584782459928719435">"Kestus:"</string>
diff --git a/packages/VpnDialogs/res/values-eu/strings.xml b/packages/VpnDialogs/res/values-eu/strings.xml
index a3b7716e..9fc16e2 100644
--- a/packages/VpnDialogs/res/values-eu/strings.xml
+++ b/packages/VpnDialogs/res/values-eu/strings.xml
@@ -18,13 +18,14 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexio bat konfiguratu nahi du sareko trafikoa gainbegiratzeko. Onartu soilik iturburuaz fidatzen bazara. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; agertzen da pantailan, VPNa aktibo dagoenean."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
     <string name="session" msgid="6470628549473641030">"Saioa:"</string>
     <string name="duration" msgid="3584782459928719435">"Iraupena:"</string>
     <string name="data_transmitted" msgid="7988167672982199061">"Bidalita:"</string>
     <string name="data_received" msgid="4062776929376067820">"Jasota:"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byte / <xliff:g id="NUMBER_1">%2$s</xliff:g> pakete"</string>
-    <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPN sarea"</string>
+    <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPNa"</string>
     <string name="always_on_disconnected_message" msgid="555634519845992917">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP_0">%1$s</xliff:g>, baina une honetan ezin da konektatu. <xliff:g id="VPN_APP_1">%1$s</xliff:g> sarera berriro konektatu ahal izan arte, sare publiko bat erabiliko du telefonoak."</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP">%1$s</xliff:g>, baina une honetan ezin da konektatu. VPN sarearen konexioa berreskuratu arte, ez duzu izango konexiorik."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
diff --git a/packages/VpnDialogs/res/values-fa/strings.xml b/packages/VpnDialogs/res/values-fa/strings.xml
index 56f847c..6fb5a00 100644
--- a/packages/VpnDialogs/res/values-fa/strings.xml
+++ b/packages/VpnDialogs/res/values-fa/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"درخواست اتصال"</string>
     <string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط در صورتی بپذیرید که به منبع آن اطمینان دارید. هنگامی که VPN فعال شد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در بالای صفحه نمایش شما نشان داده می‌شود."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط درصورتی‌که به منبع اعتماد دارید قبول کنید. وقتی VPN فعال باشد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در صفحه‌نمایش نشان داده می‌شود."</string>
     <string name="legacy_title" msgid="192936250066580964">"‏VPN متصل است"</string>
     <string name="session" msgid="6470628549473641030">"جلسه:"</string>
     <string name="duration" msgid="3584782459928719435">"مدت زمان:"</string>
diff --git a/packages/VpnDialogs/res/values-fi/strings.xml b/packages/VpnDialogs/res/values-fi/strings.xml
index 91c918a..8abca06 100644
--- a/packages/VpnDialogs/res/values-fi/strings.xml
+++ b/packages/VpnDialogs/res/values-fi/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Yhteyspyyntö"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> haluaa tehdä asetukset VPN-yhteydellä, jonka kautta sovellus voi valvoa verkkoliikennettä. Hyväksy vain, jos lähde on luotettava. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy ruudun yläreunassa, kun VPN on käytössä."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> haluaa muodostaa VPN-yhteyden, jonka avulla se voi valvoa verkkoliikennettä. Salli tämä vain, jos luotat lähteeseen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy näytölläsi, kun VPN on aktiivinen."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN on yhdistetty"</string>
     <string name="session" msgid="6470628549473641030">"Käyttökerta"</string>
     <string name="duration" msgid="3584782459928719435">"Kesto:"</string>
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index aa86c7c..876111c 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt;&lt;br /&gt;&lt;img src=vpn_icon/&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion  RPV est active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
     <string name="legacy_title" msgid="192936250066580964">"RPV connecté"</string>
     <string name="session" msgid="6470628549473641030">"Session :"</string>
     <string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-fr/strings.xml b/packages/VpnDialogs/res/values-fr/strings.xml
index 7180119..27ebfb0 100644
--- a/packages/VpnDialogs/res/values-fr/strings.xml
+++ b/packages/VpnDialogs/res/values-fr/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche en haut de votre écran lorsqu\'une connexion VPN est active."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche à l\'écran lorsqu\'un VPN est actif."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN connecté"</string>
     <string name="session" msgid="6470628549473641030">"Session :"</string>
     <string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-gl/strings.xml b/packages/VpnDialogs/res/values-gl/strings.xml
index 8a66d08..cd8ee8d 100644
--- a/packages/VpnDialogs/res/values-gl/strings.xml
+++ b/packages/VpnDialogs/res/values-gl/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitude de conexión"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permite controlar o tráfico da rede. Acepta soamente se confías na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior da pantalla cando se activa a VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"A aplicación <xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permita supervisar o tráfico de rede. Acepta só se confías nela. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na pantalla cando a VPN está activa."</string>
     <string name="legacy_title" msgid="192936250066580964">"A VPN está conectada"</string>
     <string name="session" msgid="6470628549473641030">"Sesión:"</string>
     <string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-gu/strings.xml b/packages/VpnDialogs/res/values-gu/strings.xml
index 961711c..b5a8831 100644
--- a/packages/VpnDialogs/res/values-gu/strings.xml
+++ b/packages/VpnDialogs/res/values-gu/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"કનેક્શન વિનંતી"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN કનેક્શન સેટ કરવા માગે છે જે તેને નેટવર્ક ટ્રાફિક મૉનિટર કરવાની મંજૂરી આપે છે. જો તમને સ્રોત પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીનની ટોચ પર ત્યારે દેખાય છે જ્યારે VPN સક્રિય હોય છે."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> એક એવું VPN કનેક્શન સેટ કરવા માગે છે કે જે તેને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરવાની મંજૂરી આપતું હોય. જો તમને સૉર્સ પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીન પર ત્યારે દેખાય છે, જ્યારે VPN સક્રિય હોય છે."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN કનેક્ટ કરેલું છે"</string>
     <string name="session" msgid="6470628549473641030">"સત્ર:"</string>
     <string name="duration" msgid="3584782459928719435">"અવધિ:"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index eed0858..c9c65d5 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्‍शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> को वीपीएन कनेक्शन सेट अप करने की अनुमति चाहिए. इससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. अनुमति तब दें, जब आपको ऐप्लिकेशन पर भरोसा हो. वीपीएन चालू होने पर, आपकी स्क्रीन पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिखेगा."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट है"</string>
     <string name="session" msgid="6470628549473641030">"सत्र:"</string>
     <string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-hr/strings.xml b/packages/VpnDialogs/res/values-hr/strings.xml
index aa9e436..576d997 100644
--- a/packages/VpnDialogs/res/values-hr/strings.xml
+++ b/packages/VpnDialogs/res/values-hr/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kada je VPN aktivan, pri vrhu zaslona prikazuje se &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kad je VPN aktivan, na zaslonu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN je spojen"</string>
     <string name="session" msgid="6470628549473641030">"Sesija"</string>
     <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-hu/strings.xml b/packages/VpnDialogs/res/values-hu/strings.xml
index 703aa79..69b999f 100644
--- a/packages/VpnDialogs/res/values-hu/strings.xml
+++ b/packages/VpnDialogs/res/values-hu/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Kapcsolódási kérés"</string>
     <string name="warning" msgid="809658604548412033">"A(z) <xliff:g id="APP">%s</xliff:g> VPN kapcsolatot akar beállítani, amelynek segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor a VPN aktív, &lt;img src=vpn_icon /&gt; ikon jelenik meg a képernyő tetején."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás VPN-kapcsolatot szeretne beállítani, amely segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor aktív a VPN, a következő ikon látható a képernyőn: &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"A VPN csatlakoztatva van"</string>
     <string name="session" msgid="6470628549473641030">"Munkamenet:"</string>
     <string name="duration" msgid="3584782459928719435">"Időtartam:"</string>
diff --git a/packages/VpnDialogs/res/values-hy/strings.xml b/packages/VpnDialogs/res/values-hy/strings.xml
index c296c85..d2a6d42 100644
--- a/packages/VpnDialogs/res/values-hy/strings.xml
+++ b/packages/VpnDialogs/res/values-hy/strings.xml
@@ -17,7 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Միացման հայց"</string>
-    <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին: Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+    <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> հավելվածն ուզում է միանալ VPN-ի ցանցին՝ թրաֆիկին հետևելու համար։ Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվացված լինի, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերակը կհայտնվի ձեր էկրանին։"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN-ը կապակցված է"</string>
     <string name="session" msgid="6470628549473641030">"Աշխատաշրջան`"</string>
     <string name="duration" msgid="3584782459928719435">"Տևողությունը՝"</string>
@@ -25,7 +26,7 @@
     <string name="data_received" msgid="4062776929376067820">"Ստացվել է՝"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> բայթ / <xliff:g id="NUMBER_1">%2$s</xliff:g> փաթեթ"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"Չի հաջողվում միանալ միշտ միացված VPN-ին"</string>
-    <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին:"</string>
+    <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին։"</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Մինչև VPN-ը նորից չմիանա, դուք կապ չեք ունենա:"</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Փոխել VPN-ի կարգավորումները"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 18ef372a..059008f 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan sambungan VPN yang memungkinkannya memantau traffic jaringan. Terima hanya jika Anda memercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di bagian atas layar Anda saat VPN aktif."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan koneksi VPN yang memungkinkannya memantau traffic jaringan. Hanya terima jika Anda memercayai sumbernya. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di layar bila VPN aktif."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN tersambung"</string>
     <string name="session" msgid="6470628549473641030">"Sesi:"</string>
     <string name="duration" msgid="3584782459928719435">"Durasi:"</string>
diff --git a/packages/VpnDialogs/res/values-is/strings.xml b/packages/VpnDialogs/res/values-is/strings.xml
index 70fb40f..a75371d 100644
--- a/packages/VpnDialogs/res/values-is/strings.xml
+++ b/packages/VpnDialogs/res/values-is/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Beiðni um tengingu"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til þess að geta fylgst með netumferð. Samþykktu þetta aðeins ef þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist efst á skjánum þegar VPN er virkt."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til að fylgjast með netumferð. Ekki samþykkja þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist á skjánum hjá þér þegar VPN er virkt."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN er tengt"</string>
     <string name="session" msgid="6470628549473641030">"Lota:"</string>
     <string name="duration" msgid="3584782459928719435">"Tímalengd:"</string>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index 2602493..c443c51 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Richiesta di connessione"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, nella parte superiore dello schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vuole configurare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, sullo schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN connessa"</string>
     <string name="session" msgid="6470628549473641030">"Sessione:"</string>
     <string name="duration" msgid="3584782459928719435">"Durata:"</string>
diff --git a/packages/VpnDialogs/res/values-iw/strings.xml b/packages/VpnDialogs/res/values-iw/strings.xml
index ebabd4e..81903d2 100644
--- a/packages/VpnDialogs/res/values-iw/strings.xml
+++ b/packages/VpnDialogs/res/values-iw/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"בקשת חיבור"</string>
     <string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> רוצה להגדיר חיבור VPN שיאפשר לו לפקח על תעבורת הרשת. אשר את הבקשה רק אם אתה נותן אמון במקור. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע בחלק העליון של המסך כאשר VPN פעיל."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‏האפליקציה <xliff:g id="APP">%s</xliff:g> מבקשת להגדיר חיבור VPN שבאמצעותו היא תנהל מעקב אחר התנועה ברשת. יש לאשר את הבקשה רק אם המקור נראה לך אמין. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע על המסך כאשר חיבור ה-VPN פעיל."</string>
     <string name="legacy_title" msgid="192936250066580964">"‏VPN מחובר"</string>
     <string name="session" msgid="6470628549473641030">"הפעלה"</string>
     <string name="duration" msgid="3584782459928719435">"משך:"</string>
diff --git a/packages/VpnDialogs/res/values-ja/strings.xml b/packages/VpnDialogs/res/values-ja/strings.xml
index 8480692..e03e9d3 100644
--- a/packages/VpnDialogs/res/values-ja/strings.xml
+++ b/packages/VpnDialogs/res/values-ja/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"接続リクエスト"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> がネットワーク トラフィックを監視するため VPN 接続をセットアップしようとしています。信頼できるソースである場合にのみ許可してください。&lt;br /&gt; &lt;br /&gt; VPN がアクティブになると画面の上部に &lt;img src=vpn_icon /&gt; が表示されます。"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> は、ネットワーク トラフィックを監視できるよう、VPN 接続を設定するよう求めています。ソースを信頼できる場合のみ、許可してください。VPN が有効になると、画面に &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; が表示されます。"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN接続済み"</string>
     <string name="session" msgid="6470628549473641030">"セッション:"</string>
     <string name="duration" msgid="3584782459928719435">"期間:"</string>
diff --git a/packages/VpnDialogs/res/values-ka/strings.xml b/packages/VpnDialogs/res/values-ka/strings.xml
index e5a0753..9c4388e 100644
--- a/packages/VpnDialogs/res/values-ka/strings.xml
+++ b/packages/VpnDialogs/res/values-ka/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"კავშირის მოთხოვნა"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. მიიღოთ მხოლოდ ისეთ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენი ეკრანის სიის თავში გამოჩნდება, როდესაც VPN აქტიურია."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>-ს სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. დათანხმდით მხოლოდ იმ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენს ეკრანზე გამოჩნდება, როდესაც VPN აქტიურია."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN დაკავშირებულია"</string>
     <string name="session" msgid="6470628549473641030">"სესია:"</string>
     <string name="duration" msgid="3584782459928719435">"ხანგრძლივობა:"</string>
diff --git a/packages/VpnDialogs/res/values-kk/strings.xml b/packages/VpnDialogs/res/values-kk/strings.xml
index 79f79c3..9a499d3 100644
--- a/packages/VpnDialogs/res/values-kk/strings.xml
+++ b/packages/VpnDialogs/res/values-kk/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Байланысты сұрау"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN байланысын орнатқысы келеді, бұл оған желілік трафикті бақылауға мүмкіндік береді. Көзге сенсеңіз ғана қабылдаңыз. VPN белсенді болғанда экранның жоғарғы жағында &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; көрсетіледі."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> қолданбасы VPN байланысын орнатқысы келеді, бұл оған желі трафигін бақылауға мүмкіндік береді. Сұрауды қабылдамас бұрын, дереккөздің сенімді екеніне көз жеткізіңіз. VPN белсенді болған кезде, экранда &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; белгішесі пайда болады."</string>
     <string name="legacy_title" msgid="192936250066580964">"ВЖЖ қосылған"</string>
     <string name="session" msgid="6470628549473641030">"Сессия:"</string>
     <string name="duration" msgid="3584782459928719435">"Ұзақтығы:"</string>
diff --git a/packages/VpnDialogs/res/values-km/strings.xml b/packages/VpnDialogs/res/values-km/strings.xml
index 06f34db..0ed2e84b 100644
--- a/packages/VpnDialogs/res/values-km/strings.xml
+++ b/packages/VpnDialogs/res/values-km/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"សំណើ​សុំ​ការ​តភ្ជាប់"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ចង់​បង្កើត​ការ​តភ្ជាប់ VPN ដែល​អនុញ្ញាត​ឲ្យ​វា​ត្រួតពិនិត្យ​ចរាចរ​បណ្ដាញ។ ព្រម​ទទួល ប្រសិនបើ​អ្នក​ទុកចិត្ត​លើ​ប្រភព​តែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; នឹង​លេចឡើង​នៅ​ផ្នែក​ខាងលើ​នៃ​អេក្រង់​របស់​អ្នក ពេល VPN សកម្ម។"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ចង់រៀបចំការតភ្ជាប់ VPN ដែលអនុញ្ញាតឱ្យវាត្រួតពិនិត្យ​ចរាចរណ៍បណ្តាញ។ យល់ព្រម ប្រសិនបើអ្នកជឿទុកចិត្តលើប្រភពនេះតែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; បង្ហាញនៅលើអេក្រង់របស់អ្នក នៅពេល VPN កំពុងដំណើរការ។"</string>
     <string name="legacy_title" msgid="192936250066580964">"បា​ន​ភ្ជាប់ VPN"</string>
     <string name="session" msgid="6470628549473641030">"សម័យ៖"</string>
     <string name="duration" msgid="3584782459928719435">"ថិរវេលា៖"</string>
diff --git a/packages/VpnDialogs/res/values-kn/strings.xml b/packages/VpnDialogs/res/values-kn/strings.xml
index 040cd6c..6308f18 100644
--- a/packages/VpnDialogs/res/values-kn/strings.xml
+++ b/packages/VpnDialogs/res/values-kn/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ಸಂಪರ್ಕ ವಿನಂತಿ"</string>
     <string name="warning" msgid="809658604548412033">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನೀವು ಮೂಲವನ್ನು ನಂಬಿದರೆ ಮಾತ್ರ ಸಮ್ಮತಿಸಿ. VPN ಸಕ್ರಿಯವಾಗಿರುವಾಗ ನಿಮ್ಮ ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ಗೋರಿಸುತ್ತದೆ."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನಿಮಗೆ ಮೂಲದ ಮೇಲೆ ನಂಬಿಕೆ ಇದ್ದರೆ ಮಾತ್ರ ಸ್ವೀಕರಿಸಿ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ಸಕ್ರಿಯವಾದ ನಂತರ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುತ್ತದೆ."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="session" msgid="6470628549473641030">"ಸೆಷನ್:"</string>
     <string name="duration" msgid="3584782459928719435">"ಅವಧಿ:"</string>
diff --git a/packages/VpnDialogs/res/values-ko/strings.xml b/packages/VpnDialogs/res/values-ko/strings.xml
index 6ad4976..6e179bb 100644
--- a/packages/VpnDialogs/res/values-ko/strings.xml
+++ b/packages/VpnDialogs/res/values-ko/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"연결 요청"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링하도록 허용하는 VPN 연결을 설정하려고 합니다. 출처를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성화되면 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;이 화면 위에 표시됩니다."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링할 수 있도록 VPN 연결을 설정하려고 합니다. 소스를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성 상태일 때는 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; 아이콘이 화면에 표시됩니다."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN이 연결되었습니다."</string>
     <string name="session" msgid="6470628549473641030">"세션:"</string>
     <string name="duration" msgid="3584782459928719435">"기간:"</string>
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 23c9be8..31f9e2d 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Туташуу сурамы"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> тармактык трафикти көзөмөлдөөгө уруксат берген VPN туташуусун орноткусу келет. Аны булакка ишенсеңиз гана кабыл алыңыз. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN иштеп турганда экраныңыздын жогору жагынан көрүнөт."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> тармак трафигин көзөмөлдөөгө уруксат берген VPN байланышын орноткусу келет. Булакка ишенсеңиз гана кабыл алыңыз. VPN иштеп жатканда, экраныңызда &lt;br /&gt; &lt;br /&gt; &amp;It;img src=vpn_icon /&gt; көрүнөт."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN байланышта"</string>
     <string name="session" msgid="6470628549473641030">"Сессия:"</string>
     <string name="duration" msgid="3584782459928719435">"Узактыгы:"</string>
diff --git a/packages/VpnDialogs/res/values-lo/strings.xml b/packages/VpnDialogs/res/values-lo/strings.xml
index c591308..cec69f0 100644
--- a/packages/VpnDialogs/res/values-lo/strings.xml
+++ b/packages/VpnDialogs/res/values-lo/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ການ​ຮ້ອງ​ຂໍ​ການ​ເຊື່ອມ​ຕໍ່"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງ​ການ​ຕັ້​ງ​ຄ່າ​ການ​ເຊື່ອມ​ຕໍ່ VPN ທີ່​ອະ​ນຸ​ຍາດ​ໃຫ້​ຕິດ​ຕາມ​ທຣາບ​ຟິກ​ເຄືອ​ຂ່າຍ​ໄດ້. ​ທ່ານ​​ຄວນ​​ຍິນຍອມ​ສະ​ເພາະ​ໃນ​ກໍ​ລະ​ນີ​ທີ່​ທ່ານ​ເຊື່ອ​ຖື​ແຫລ່ງ​ຂໍ້​ມູນ​ເທົ່າ​ນັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະ​ປາ​ກົດ​ຢູ່​ດ້ານ​ເທິງ​ຂອງ​ໜ້າ​ຈໍ​ເມື່ອ​ມີ​ການ​ເປີດ​ໃຊ້ VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ມັນກວດກາການ​ຈາ​ລະ​ຈອນ​ເຄືອ​ຂ່າ​ຍໄດ້. ໃຫ້ຍອມຮັບສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫຼ່ງທີ່ມາເທົ່ານັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະປາກົດຢູ່ໜ້າຈໍຂອງທ່ານເມື່ອເປີດໃຊ້ VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
     <string name="session" msgid="6470628549473641030">"ເຊສຊັນ:"</string>
     <string name="duration" msgid="3584782459928719435">"ໄລຍະເວລາ:"</string>
diff --git a/packages/VpnDialogs/res/values-lt/strings.xml b/packages/VpnDialogs/res/values-lt/strings.xml
index 8846310..97abd0d 100644
--- a/packages/VpnDialogs/res/values-lt/strings.xml
+++ b/packages/VpnDialogs/res/values-lt/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Ryšio užklausa"</string>
     <string name="warning" msgid="809658604548412033">"„<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; rodoma ekrano viršuje, kai VPN aktyvus."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; piktograma rodoma ekrane, kai VPN aktyvus."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN prijungtas"</string>
     <string name="session" msgid="6470628549473641030">"Sesija"</string>
     <string name="duration" msgid="3584782459928719435">"Trukmė:"</string>
diff --git a/packages/VpnDialogs/res/values-lv/strings.xml b/packages/VpnDialogs/res/values-lv/strings.xml
index 07625b6..6341fbd 100644
--- a/packages/VpnDialogs/res/values-lv/strings.xml
+++ b/packages/VpnDialogs/res/values-lv/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Savienojuma pieprasījums"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; tiek rādīta ekrāna augšdaļā, kad darbojas VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Lietotne <xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; Ekrānā tiek rādīta ikona &lt;img src=vpn_icon /&gt;, kad darbojas VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"Ir izveidots savienojums ar VPN"</string>
     <string name="session" msgid="6470628549473641030">"Sesija:"</string>
     <string name="duration" msgid="3584782459928719435">"Ilgums:"</string>
diff --git a/packages/VpnDialogs/res/values-mk/strings.xml b/packages/VpnDialogs/res/values-mk/strings.xml
index b5a64f2..689d028 100644
--- a/packages/VpnDialogs/res/values-mk/strings.xml
+++ b/packages/VpnDialogs/res/values-mk/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Барање за поврзување"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со ВПН коешто му дозволува да го набљудува сообраќајот на мрежата. Прифатете само доколку му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се појавува на врвот на екранот кога ВПН е активна."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со VPN што ќе дозволи да го набљудува сообраќајот на мрежата. Прифатете само ако му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ќе се појави на екранот кога ќе се активира VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN е поврзана"</string>
     <string name="session" msgid="6470628549473641030">"Сесија:"</string>
     <string name="duration" msgid="3584782459928719435">"Времетраење:"</string>
@@ -30,7 +31,7 @@
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Променете ги поставките за VPN"</string>
     <string name="configure" msgid="4905518375574791375">"Конфигурирај"</string>
-    <string name="disconnect" msgid="971412338304200056">"Исклучи"</string>
+    <string name="disconnect" msgid="971412338304200056">"Прекини врска"</string>
     <string name="open_app" msgid="3717639178595958667">"Отвори ја апликацијата"</string>
     <string name="dismiss" msgid="6192859333764711227">"Отфрли"</string>
 </resources>
diff --git a/packages/VpnDialogs/res/values-ml/strings.xml b/packages/VpnDialogs/res/values-ml/strings.xml
index 680d0ef..8284a78 100644
--- a/packages/VpnDialogs/res/values-ml/strings.xml
+++ b/packages/VpnDialogs/res/values-ml/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"കണക്ഷൻ അഭ്യർത്ഥന"</string>
     <string name="warning" msgid="809658604548412033">"നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ <xliff:g id="APP">%s</xliff:g> സജ്ജീകരിക്കേണ്ടതുണ്ട്. ഉറവിടം പരിചിതമാണെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകളിൽ ദൃശ്യമാകുന്നു."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"നെറ്റ്‌വർക്ക് ട്രാഫിക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ സജ്ജീകരിക്കാൻ <xliff:g id="APP">%s</xliff:g> താൽപ്പര്യപ്പെടുന്നു. നിങ്ങൾ ഉറവിടം വിശ്വസിക്കുന്നുണ്ടെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകും."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN കണക്‌റ്റുചെയ്‌തു"</string>
     <string name="session" msgid="6470628549473641030">"സെഷൻ:"</string>
     <string name="duration" msgid="3584782459928719435">"സമയദൈര്‍ഘ്യം:"</string>
diff --git a/packages/VpnDialogs/res/values-mn/strings.xml b/packages/VpnDialogs/res/values-mn/strings.xml
index 9aa104a..1dd4c15 100644
--- a/packages/VpnDialogs/res/values-mn/strings.xml
+++ b/packages/VpnDialogs/res/values-mn/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Холболтын хүсэлт"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> нь сүлжээний трафикыг хянах боломж бүхий VPN холболт үүсгэхийг хүсэж байна. Та зөвхөн эх үүсвэрт итгэж байгаа бол зөвшөөрнө үү. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; таны дэлгэц дээр VPN идэвхтэй үед гарч ирнэ."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> нь түүнд сүлжээний ачааллыг хянах боломжийг олгодог VPN холболт тохируулахыг хүсэж байна. Зөвхөн та эх сурвалжид итгэдэг тохиолдолд зөвшөөрнө үү. VPN идэвхтэй үед таны дэлгэц дээр &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; харагдана."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN холбогдов"</string>
     <string name="session" msgid="6470628549473641030">"Сешн:"</string>
     <string name="duration" msgid="3584782459928719435">"Үргэлжлэх хугацаа:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 41d7429..22fb502 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -18,18 +18,19 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"कनेक्‍शन विनंती"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्‍यासाठी त्यास अनुमती देणारे VPN कनेक्‍शन सेट करू इच्‍छितो. तुम्हाला स्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN सक्रिय असताना आपल्‍या स्क्रीनच्या शीर्षावर दिसते."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ला नेटवर्क ट्रॅफिकवर लक्ष ठेवण्याची अनुमती देणारे VPN कनेक्‍शन सेट करायचे आहे. तुमचा स्रोतावर विश्वास असेल तरच स्वीकारा. VPN अ‍ॅक्टिव्ह असल्यास, तुमच्या स्क्रीनवर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिसते."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट केले"</string>
     <string name="session" msgid="6470628549473641030">"सत्र:"</string>
     <string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
     <string name="data_transmitted" msgid="7988167672982199061">"प्रेषित:"</string>
     <string name="data_received" msgid="4062776929376067820">"प्राप्त झाले:"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइट / <xliff:g id="NUMBER_1">%2$s</xliff:g> पॅकेट"</string>
-    <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम चालू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
+    <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम सुरू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
     <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. <xliff:g id="VPN_APP_1">%1$s</xliff:g> शी पुन्हा कनेक्ट होईपर्यंत तुमचा फोन सार्वजनिक नेटवर्क वापरेल."</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
-    <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
+    <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
     <string name="disconnect" msgid="971412338304200056">"‍डिस्कनेक्ट करा"</string>
     <string name="open_app" msgid="3717639178595958667">"अ‍ॅप उघडा"</string>
     <string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/VpnDialogs/res/values-ms/strings.xml b/packages/VpnDialogs/res/values-ms/strings.xml
index b489f2e..c9961d2 100644
--- a/packages/VpnDialogs/res/values-ms/strings.xml
+++ b/packages/VpnDialogs/res/values-ms/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl memantau trafik rangkaian. Terima hanya jika anda mempercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; terpapar pada bahagian atas skrin anda apabila VPN aktif."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl tersebut memantau trafik rangkaian. Hanya terima jika anda mempercayai sumber tersebut. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul pada skrin anda apabila VPN aktif."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN telah disambungkan"</string>
     <string name="session" msgid="6470628549473641030">"Sesi:"</string>
     <string name="duration" msgid="3584782459928719435">"Tempoh:"</string>
diff --git a/packages/VpnDialogs/res/values-my/strings.xml b/packages/VpnDialogs/res/values-my/strings.xml
index 9d60ff4..36348c8 100644
--- a/packages/VpnDialogs/res/values-my/strings.xml
+++ b/packages/VpnDialogs/res/values-my/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ချိတ်ဆက်ရန် တောင်းဆိုချက်"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက် လုပ်ငန်းကို စောင့်ကြည့်ခွင့် ပြုမည့် VPN ချိတ်ဆက်မှုကို ထူထောင်လိုသည်။ ရင်းမြစ်ကို သင်က ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; မှာ VPN အလုပ်လုပ်နေလျှင် သင်၏ မျက်နှာပြင် ထိပ်မှာ ပေါ်လာမည်။"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်ရန် ခွင့်ပြုသည့် VPN ချိတ်ဆက်မှုကို စနစ်ထည့်သွင်းလိုသည်။ ဤရင်းမြစ်ကို သင်ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; သည် VPN ဖွင့်ထားသောအခါ သင့်ဖန်သားပြင်တွင် ပေါ်ပါသည်။"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPNနှင့်ချိတ်ဆက်ထားသည်"</string>
     <string name="session" msgid="6470628549473641030">"သတ်မှတ်ပေးထားသည့်အချိန်:"</string>
     <string name="duration" msgid="3584782459928719435">"အချိန်ကာလ-"</string>
@@ -30,7 +31,7 @@
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ဆက်တင်များ ပြောင်းရန်"</string>
     <string name="configure" msgid="4905518375574791375">"ပုံပေါ်စေသည်"</string>
-    <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်ခြင်းရပ်ရန်"</string>
+    <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
     <string name="open_app" msgid="3717639178595958667">"အက်ပ်ကို ဖွင့်ရန်"</string>
     <string name="dismiss" msgid="6192859333764711227">"ပယ်ရန်"</string>
 </resources>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index be572d4..14c84d7 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Tilkoblingsforespørsel"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ønsker å bruke en VPN-tilkobling som tillater at appen overvåker nettverkstrafikken. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på skjermen din når VPN er aktivert."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-tilkobling som lar appen overvåke nettverkstrafikk. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på skjermen når VPN er aktivert."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN er tilkoblet"</string>
     <string name="session" msgid="6470628549473641030">"Økt:"</string>
     <string name="duration" msgid="3584782459928719435">"Varighet:"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index b716c35..2a5648d 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"जडान अनुरोध"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ले नेटवर्क यातायात अनुगमन गर्न अनुमति दिने VPN जडान स्थापना गर्न चाहन्छ। तपाईँले स्रोत भरोसा छ भने मात्र स्वीकार गर्नुहोस्। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; जब VPN सक्रिय हुन्छ आफ्नो स्क्रिनको माथि देखा पर्छन्।"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ले कुनै VPN कनेक्सन सेटअप गर्न चाहन्छ। यसको सहायताले यो एप नेटवर्क ट्राफिकको निगरानी राख्न सक्छ। तपाईं यो एपमाथि विश्वास गर्नुहुन्छ भने मात्र स्वीकार गर्नुहोस्। VPN सक्रिय हुँदा तपाईंको स्क्रिनमा &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; देखा पर्छ।"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN जोडिएको छ"</string>
     <string name="session" msgid="6470628549473641030">"सत्र:"</string>
     <string name="duration" msgid="3584782459928719435">"अवधि:"</string>
@@ -30,7 +31,7 @@
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
     <string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
-    <string name="disconnect" msgid="971412338304200056">"विच्छेदन गर्नुहोस्"</string>
-    <string name="open_app" msgid="3717639178595958667">"अनुप्रयोग खोल्नुहोस्"</string>
+    <string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट गर्नुहोस्"</string>
+    <string name="open_app" msgid="3717639178595958667">"एप खोल्नुहोस्"</string>
     <string name="dismiss" msgid="6192859333764711227">"खारेज गर्नुहोस्"</string>
 </resources>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 8073b09..33f8a89 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verschijnt op je scherm als het VPN actief is."</string>
     <string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
     <string name="session" msgid="6470628549473641030">"Sessie:"</string>
     <string name="duration" msgid="3584782459928719435">"Duur:"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index f1122eb..0604b47 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ସଂଯୋଗ ଅନୁରୋଧ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହି ନେଟ୍‌ୱର୍କର ଟ୍ରାଫିକକୁ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦିଏ। ଆପଣ ସୋର୍ସ ଉପରେ ବିଶ୍ୱାସ କରିବା ବଦଳରେ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବାବେଳେ ଏହା ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‍ର ଉପରେ ଦେଖାଯାଏ।"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହାକୁ ନେଟୱାର୍କ ଟ୍ରାଫିକ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଯଦି ଆପଣ ସୋର୍ସରେ ବିଶ୍ୱାସ କରୁଛନ୍ତି, ତେବେ ହିଁ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ ଉପରେ ଦେଖାଯାଏ।"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
     <string name="session" msgid="6470628549473641030">"ସେସନ୍‍:"</string>
     <string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
@@ -28,7 +29,7 @@
     <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇ ରହିବା ପାଇଁ ସେଟଅପ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ଫୋନ୍‍, <xliff:g id="VPN_APP_1">%1$s</xliff:g> ସହ କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଏକ ପବ୍ଲିକ୍‍ ନେଟ୍‌ୱର୍କ ବ୍ୟବହାର କରିବ।"</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇରହିବାକୁ ସେଟଅପ୍‍ କରାଯାଇଛି, କିନ୍ତୁ ଏହା ବର୍ତ୍ତମାନ କନେକ୍ଟ କରିପାରୁ ନାହିଁ। VPN ପୁଣି କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର କୌଣସି କନେକ୍ସନ୍‌ ରହିବନାହିଁ।"</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
-    <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ"</string>
+    <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ୍ ବଦଳାନ୍ତୁ"</string>
     <string name="configure" msgid="4905518375574791375">"କନଫିଗର୍‍ କରନ୍ତୁ"</string>
     <string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
     <string name="open_app" msgid="3717639178595958667">"ଆପ୍‌ ଖୋଲନ୍ତୁ"</string>
diff --git a/packages/VpnDialogs/res/values-pa/strings.xml b/packages/VpnDialogs/res/values-pa/strings.xml
index 1815f4f..d2eba0f 100644
--- a/packages/VpnDialogs/res/values-pa/strings.xml
+++ b/packages/VpnDialogs/res/values-pa/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ਕਨੈਕਸ਼ਨ ਬੇਨਤੀ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਕੇਵਲ ਤਾਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇਕਰ ਤੁਸੀਂ ਸਰੋਤ ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਟੌਪ ਤੇ ਪ੍ਰਗਟ ਹੁੰਦਾ ਹੈ ਜਦੋਂ VPN ਸਕਿਰਿਆ ਹੁੰਦਾ ਹੈ।"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ਕਿਸੇ ਅਜਿਹੇ VPN ਕਨੈਕਸ਼ਨ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਸਿਰਫ਼ ਉਦੋਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇ ਤੁਹਾਨੂੰ ਸਰੋਤ \'ਤੇ ਭਰੋਸਾ ਹੈ। VPN ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ \'ਤੇ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦਾ ਹੈ।"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
     <string name="session" msgid="6470628549473641030">"ਸੈਸ਼ਨ:"</string>
     <string name="duration" msgid="3584782459928719435">"ਮਿਆਦ:"</string>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index d5201d7..82161d3 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
     <string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br /&gt;Gdy sieć VPN jest aktywna, u góry ekranu pojawia się &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu w sieci. Zaakceptuj, jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br Gdy sieć VPN jest aktywna, na ekranie pojawia się ikona /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
     <string name="session" msgid="6470628549473641030">"Sesja:"</string>
     <string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rBR/strings.xml b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt-rBR/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
     <string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
     <string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
     <string name="session" msgid="6470628549473641030">"Sessão:"</string>
     <string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rPT/strings.xml b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
index 01beddb..a310104 100644
--- a/packages/VpnDialogs/res/values-pt-rPT/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Pedido de ligação"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior do seu ecrã quando a VPN está ativa."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"A app <xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece no ecrã quando a VPN está ativa."</string>
     <string name="legacy_title" msgid="192936250066580964">"A VPN está ligada"</string>
     <string name="session" msgid="6470628549473641030">"Sessão"</string>
     <string name="duration" msgid="3584782459928719435">"Duração:"</string>
@@ -25,12 +26,12 @@
     <string name="data_received" msgid="4062776929376067820">"Recebidos:"</string>
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bytes / <xliff:g id="NUMBER_1">%2$s</xliff:g> pacotes"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"Não é possível estabelecer ligação à VPN sempre ativada"</string>
-    <string name="always_on_disconnected_message" msgid="555634519845992917">"A aplicação <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à aplicação <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
-    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A aplicação <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
+    <string name="always_on_disconnected_message" msgid="555634519845992917">"A app <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à app <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A app <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Alterar as definições da VPN"</string>
     <string name="configure" msgid="4905518375574791375">"Configurar"</string>
     <string name="disconnect" msgid="971412338304200056">"Desligar"</string>
-    <string name="open_app" msgid="3717639178595958667">"Abrir aplicação"</string>
+    <string name="open_app" msgid="3717639178595958667">"Abrir app"</string>
     <string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
 </resources>
diff --git a/packages/VpnDialogs/res/values-pt/strings.xml b/packages/VpnDialogs/res/values-pt/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt/strings.xml
+++ b/packages/VpnDialogs/res/values-pt/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
     <string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
     <string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
     <string name="session" msgid="6470628549473641030">"Sessão:"</string>
     <string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4e60df2..5bda87e 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se afișează în partea de sus a ecranului."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; va apărea pe ecran atunci când conexiunea VPN este activă."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
     <string name="session" msgid="6470628549473641030">"Sesiune:"</string>
     <string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index f8fcfb8..ce099562 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
     <string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. Когда подключение к сети VPN активно, на экране появляется значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
     <string name="session" msgid="6470628549473641030">"Сеанс:"</string>
     <string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-si/strings.xml b/packages/VpnDialogs/res/values-si/strings.xml
index bb97a5d..a836bae 100644
--- a/packages/VpnDialogs/res/values-si/strings.xml
+++ b/packages/VpnDialogs/res/values-si/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"සම්බන්ධතා ඉල්ලීම"</string>
     <string name="warning" msgid="809658604548412033">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> අවශ්‍යය වේ. ප්‍රභවය ඔබ විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. VPN සක්‍රිය විට &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> හට අවශ්‍ය වේ. ඔබ මූලාශ්‍රය විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN සක්‍රිය විට ඔබගේ තිරයෙහි දිස් වේ."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN සම්බන්ධිතයි"</string>
     <string name="session" msgid="6470628549473641030">"සැසිය:"</string>
     <string name="duration" msgid="3584782459928719435">"කාල සීමාව:"</string>
diff --git a/packages/VpnDialogs/res/values-sk/strings.xml b/packages/VpnDialogs/res/values-sk/strings.xml
index a08117a..766c139 100644
--- a/packages/VpnDialogs/res/values-sk/strings.xml
+++ b/packages/VpnDialogs/res/values-sk/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Žiadosť o pripojenie"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> žiada o nastavenie pripojenia VPN, pomocou ktorého bude môcť sledovať sieťové prenosy. Povoľte iba v prípade, že zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa zobrazuje v hornej časti obrazovky, keď je pripojenie VPN aktívne."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce nastaviť pripojenie k sieti VPN, ktoré jej umožňuje sledovať sieťovú premávku. Povoľte to iba v prípade, ak zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; Keď je sieť VPN aktívna, na obrazovke sa zobrazí ikona &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"Sieť VPN je pripojená"</string>
     <string name="session" msgid="6470628549473641030">"Relácia"</string>
     <string name="duration" msgid="3584782459928719435">"Trvanie:"</string>
diff --git a/packages/VpnDialogs/res/values-sl/strings.xml b/packages/VpnDialogs/res/values-sl/strings.xml
index d5014fa34..361a5fa 100644
--- a/packages/VpnDialogs/res/values-sl/strings.xml
+++ b/packages/VpnDialogs/res/values-sl/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Zahteva za povezavo"</string>
     <string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, se na vrhu zaslona prikaže ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki ji omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, je na zaslonu prikazana ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
     <string name="session" msgid="6470628549473641030">"Seja:"</string>
     <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-sq/strings.xml b/packages/VpnDialogs/res/values-sq/strings.xml
index 4a96e7b..0b4ce4df 100644
--- a/packages/VpnDialogs/res/values-sq/strings.xml
+++ b/packages/VpnDialogs/res/values-sq/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Kërkesë për lidhje"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN-je që e lejon të monitorojë trafikun e rrjetit. Prano vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në krye të ekranit kur VPN-ja është aktive."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN që i lejon të monitorojë trafikun e rrjetit. Pranoje vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në ekranin tënd kur është aktive VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN-ja është e lidhur"</string>
     <string name="session" msgid="6470628549473641030">"Sesioni:"</string>
     <string name="duration" msgid="3584782459928719435">"Kohëzgjatja:"</string>
diff --git a/packages/VpnDialogs/res/values-sr/strings.xml b/packages/VpnDialogs/res/values-sr/strings.xml
index 8ce8060..01bd4df 100644
--- a/packages/VpnDialogs/res/values-sr/strings.xml
+++ b/packages/VpnDialogs/res/values-sr/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује у врху екрана када је VPN активан."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује на екрану када је VPN активан."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
     <string name="session" msgid="6470628549473641030">"Сесија:"</string>
     <string name="duration" msgid="3584782459928719435">"Трајање:"</string>
diff --git a/packages/VpnDialogs/res/values-sv/strings.xml b/packages/VpnDialogs/res/values-sv/strings.xml
index 16b6a31..60ed752 100644
--- a/packages/VpnDialogs/res/values-sv/strings.xml
+++ b/packages/VpnDialogs/res/values-sv/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Anslutningsförfrågan"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill starta en VPN-anslutning som tillåter att appen övervakar nätverkstrafiken. Godkänn endast detta om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas längst upp på skärmen när VPN-anslutningen är aktiv."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill skapa en VPN-anslutning så att den kan övervaka nätverkstrafik. Godkänn bara om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas på skärmen när VPN-anslutningen är aktiv."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN är anslutet"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Längd:"</string>
diff --git a/packages/VpnDialogs/res/values-sw/strings.xml b/packages/VpnDialogs/res/values-sw/strings.xml
index ea26884..c4f4662 100644
--- a/packages/VpnDialogs/res/values-sw/strings.xml
+++ b/packages/VpnDialogs/res/values-sw/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Ombi la muunganisho"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> inataka kusanidi muunganisho wa VPN utakaoiruhusu kufuatilia shughuli kwenye mtandao. Kubali ikiwa tu unakiamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana sehemu ya juu ya skrini yako VPN inapofanya kazi."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> inagependa kuweka mipangilio ya muunganisho wa VPN inayoiruhusu kufuatilia trafiki ya mtandao. Kubali tu iwapo unaamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana kwenye skrini yako VPN inapotumika."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN imeunganishwa"</string>
     <string name="session" msgid="6470628549473641030">"Kipindi:"</string>
     <string name="duration" msgid="3584782459928719435">"Muda:"</string>
diff --git a/packages/VpnDialogs/res/values-ta/strings.xml b/packages/VpnDialogs/res/values-ta/strings.xml
index 3b4cc57..1385bdc 100644
--- a/packages/VpnDialogs/res/values-ta/strings.xml
+++ b/packages/VpnDialogs/res/values-ta/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"இணைப்புக் கோரிக்கை"</string>
     <string name="warning" msgid="809658604548412033">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க வசதியாக VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> கோருகிறது. நம்பகமான மூலத்தை மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயக்கத்தில் உள்ளபோது திரையின் மேல் பகுதியில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க அனுமதிக்கும் VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> விரும்புகிறது. நம்பகமான VPN ஆப்ஸாக இருந்தால் மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயங்கும்போது உங்கள் திரையில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN இணைக்கப்பட்டது"</string>
     <string name="session" msgid="6470628549473641030">"அமர்வு:"</string>
     <string name="duration" msgid="3584782459928719435">"காலஅளவு:"</string>
diff --git a/packages/VpnDialogs/res/values-te/strings.xml b/packages/VpnDialogs/res/values-te/strings.xml
index 864c926..2316c62 100644
--- a/packages/VpnDialogs/res/values-te/strings.xml
+++ b/packages/VpnDialogs/res/values-te/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"కనెక్షన్ అభ్యర్థన"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించగలగడానికి VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు మూలాన్ని విశ్వసిస్తే మాత్రమే ఆమోదించండి. VPN సక్రియంగా ఉన్నప్పుడు మీ స్క్రీన్ ఎగువన &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; కనిపిస్తుంది."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"నెట్‌వర్క్ ట్రాఫిక్‌ను పర్యవేక్షించగలగడానికి, <xliff:g id="APP">%s</xliff:g> VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు సోర్స్‌ను విశ్వసిస్తే మాత్రమే ఆమోదించండి. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN యాక్టివ్‌గా ఉన్నప్పుడు మీ స్క్రీన్ పై కనిపిస్తుంది."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN కనెక్ట్ చేయబడింది"</string>
     <string name="session" msgid="6470628549473641030">"సెషన్:"</string>
     <string name="duration" msgid="3584782459928719435">"వ్యవధి:"</string>
diff --git a/packages/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index 333ff5f..2e174cd 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ต้องการตั้งค่าการเชื่อมต่อ VPN เพื่อให้ตรวจสอบการจราจรของข้อมูลในเครือข่ายได้ ยอมรับต่อเมื่อคุณไว้วางใจแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏบนหน้าจอเมื่อใช้งาน VPN อยู่"</string>
     <string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
     <string name="session" msgid="6470628549473641030">"เซสชัน"</string>
     <string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/VpnDialogs/res/values-tl/strings.xml b/packages/VpnDialogs/res/values-tl/strings.xml
index 9c01c32..ea69fba 100644
--- a/packages/VpnDialogs/res/values-tl/strings.xml
+++ b/packages/VpnDialogs/res/values-tl/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Kahilingan sa koneksyon"</string>
     <string name="warning" msgid="809658604548412033">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko ng network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa itaas ng iyong screen kapag aktibo ang VPN."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko sa network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa iyong screen kapag aktibo ang VPN."</string>
     <string name="legacy_title" msgid="192936250066580964">"Nakakonekta ang VPN"</string>
     <string name="session" msgid="6470628549473641030">"Session:"</string>
     <string name="duration" msgid="3584782459928719435">"Tagal:"</string>
diff --git a/packages/VpnDialogs/res/values-tr/strings.xml b/packages/VpnDialogs/res/values-tr/strings.xml
index 8665a47..7ffa4bc 100644
--- a/packages/VpnDialogs/res/values-tr/strings.xml
+++ b/packages/VpnDialogs/res/values-tr/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Bağlantı isteği"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ağ trafiğini izlemesine olanak veren bir VPN bağlantısı oluşturmak istiyor. Sadece, ilgili kaynağa güveniyorsanız kabul edin. &lt;br /&gt; &lt;br /&gt; VPN aktif olduğunda ekranınızın üst tarafında &lt;img src=vpn_icon /&gt; simgesi görünür."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>, ağ trafiğini izlemesine izin veren bir VPN bağlantısı oluşturmak istiyor. Yalnızca kaynağa güveniyorsanız kabul edin. VPN etkin olduğunda ekranınızda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; görünür."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN bağlı"</string>
     <string name="session" msgid="6470628549473641030">"Oturum:"</string>
     <string name="duration" msgid="3584782459928719435">"Süre:"</string>
diff --git a/packages/VpnDialogs/res/values-uk/strings.xml b/packages/VpnDialogs/res/values-uk/strings.xml
index 8f91abf..6411d7c 100644
--- a/packages/VpnDialogs/res/values-uk/strings.xml
+++ b/packages/VpnDialogs/res/values-uk/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Запит на під’єднання"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Дозволяйте, якщо довіряєте джерелу. Коли мережа VPN активна, угорі екрана відображається значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Надавайте дозвіл, лише якщо довіряєте джерелу. Коли мережа VPN активна, на екрані з’являється значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"Мережу VPN під’єднано"</string>
     <string name="session" msgid="6470628549473641030">"Сеанс:"</string>
     <string name="duration" msgid="3584782459928719435">"Тривалість:"</string>
diff --git a/packages/VpnDialogs/res/values-ur/strings.xml b/packages/VpnDialogs/res/values-ur/strings.xml
index db0c297..3a23e94 100644
--- a/packages/VpnDialogs/res/values-ur/strings.xml
+++ b/packages/VpnDialogs/res/values-ur/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"کنکشن کی درخواست"</string>
     <string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن ترتیب دینا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہے۔ اگر آپ کو ماخذ پر بھروسہ ہے تبھی قبول کریں۔ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; آپ کی اسکرین کے اوپر اس وقت ظاہر ہوتا ہے جب VPN فعال ہوتا ہے۔"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن سیٹ اپ کرنا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہو۔ آپ کو ماخذ پر اعتماد ہونے پر ہی قبول کریں۔ ‎&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ‏VPN کے فعال ہونے پر آپ کی اسکرین پر ظاہر ہوتا ہے۔"</string>
     <string name="legacy_title" msgid="192936250066580964">"‏VPN مربوط ہے"</string>
     <string name="session" msgid="6470628549473641030">"سیشن:"</string>
     <string name="duration" msgid="3584782459928719435">"دورانیہ:"</string>
diff --git a/packages/VpnDialogs/res/values-uz/strings.xml b/packages/VpnDialogs/res/values-uz/strings.xml
index 5a348a0..a3256e7 100644
--- a/packages/VpnDialogs/res/values-uz/strings.xml
+++ b/packages/VpnDialogs/res/values-uz/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Ulanish uchun so‘rov"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ilovasi trafikni kuzatish uchun VPN tarmog‘iga ulanmoqchi. Agar ilovaga ishonsangiz, so‘rovga rozi bo‘ling.&lt;br /&gt; &lt;br /&gt;VPN faol bo‘lsa, ekranning yuqori qismida &lt;img src=vpn_icon /&gt; belgisi paydo bo‘ladi."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ilovasi tarmoqdagi trafikni kuzatish uchun VPN aloqasini sozlamoqchi. Agar unga ishonsangiz, ruxsat bering. VPN aloqa faolligida ekranda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; chiqadi."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ulangan"</string>
     <string name="session" msgid="6470628549473641030">"Seans:"</string>
     <string name="duration" msgid="3584782459928719435">"Davomiyligi:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9ae..184d08d 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. &lt;br /&gt; &lt;br /&gt; Biểu tượng &lt;img src=vpn_icon /&gt; xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Bạn chỉ nên chấp nhận nếu tin tưởng nguồn đó. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sẽ xuất hiện trên màn hình khi VPN đang hoạt động."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
     <string name="session" msgid="6470628549473641030">"Phiên"</string>
     <string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 7e528bd..a7262be 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
     <string name="warning" msgid="809658604548412033">"“<xliff:g id="APP">%s</xliff:g>”想要设置一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请勿接受此请求。&lt;br /&gt; &lt;br /&gt;启用 VPN 后,屏幕顶部会出现一个 &lt;img src=vpn_icon /&gt; 图标。"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"“<xliff:g id="APP">%s</xliff:g>”想要建立一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请不要接受。VPN 处于启用状态时,屏幕上会显示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
     <string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
     <string name="session" msgid="6470628549473641030">"会话:"</string>
     <string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rHK/strings.xml b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
index 49605b0..e4e6432 100644
--- a/packages/VpnDialogs/res/values-zh-rHK/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"連線要求"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。VPN 啟用時,畫面會顯示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
     <string name="session" msgid="6470628549473641030">"時段:"</string>
     <string name="duration" msgid="3584782459928719435">"持續時間︰"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rTW/strings.xml b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
index edd8e61..f54ca4a 100644
--- a/packages/VpnDialogs/res/values-zh-rTW/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"連線要求"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線,允許此要求即開放該來源監控網路流量。除非你信任該來源,否則請勿任意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線,以便監控網路流量。除非你信任該來源,否則請勿接受要求。&lt;br /&gt; &lt;br /&gt; VPN 啟用時,畫面上會顯示 &lt;img src=vpn_icon /&gt;。"</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
     <string name="session" msgid="6470628549473641030">"工作階段:"</string>
     <string name="duration" msgid="3584782459928719435">"持續時間:"</string>
diff --git a/packages/VpnDialogs/res/values-zu/strings.xml b/packages/VpnDialogs/res/values-zu/strings.xml
index 4ab1225..c224b13 100644
--- a/packages/VpnDialogs/res/values-zu/strings.xml
+++ b/packages/VpnDialogs/res/values-zu/strings.xml
@@ -18,6 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Isicelo soxhumo"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumo lwe-VPN eyivumela ukwengamela ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ibonakala phezu kwesikrini sakho uma i-VPN isebenza."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"I-<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumano lwe-VPN oluyivumela ukuthi igade ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ivela kusikrini sakho lapho i-VPN isebenza."</string>
     <string name="legacy_title" msgid="192936250066580964">"I-VPN ixhunyiwe"</string>
     <string name="session" msgid="6470628549473641030">"Iseshini:"</string>
     <string name="duration" msgid="3584782459928719435">"Ubude besikhathi:"</string>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..adc3086
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Lewer programme onder uitsnede-area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..648e1d4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ከተቆረጠው አከባቢ በታች የመተግበሪያዎች ምስልን ስራ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 0000000..2d3b506
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"عرض التطبيقات أسفل منطقة الصورة المقطوعة للشاشة"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 0000000..db2b15a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"এপ্‌সমূহ কাটআউট অঞ্চলৰ তলত দেখুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 0000000..a6b7c43
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Tətbiqləri kəsilmə sahəsinin aşağısında göstərin"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..f80fa8d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Prikazuj aplikacije ispod oblasti izreza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 0000000..0e5c8bc
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Візуалізацыя праграм ніжэй месца выраза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..e97bb57
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Изобразяване на приложенията под областта на прореза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..d13c777
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"কাটআউট এরিয়ার নিচে অ্যাপ রেন্ডার করুন"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..9c9f437
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderovanje aplikacija ispod izrezanog područja"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 0000000..e0a577e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderitza les aplicacions per sota de l\'àrea de retallada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..0f64473
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykreslovat aplikace pod oblastí výseče"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..d0cc43e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gengiv apps under skærmhakkets område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..a7759ea
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps unterhalb des Aussparungs-Bereichs darstellen"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..b71679a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Απόδοση εφαρμογών κάτω από την περιοχή εγκοπής"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..8c85cbd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..8c85cbd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..8c85cbd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..8c85cbd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..8b72d9f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎Render apps below cutout area‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..359cdd0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps debajo del área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..47f525e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicaciones por debajo de la zona de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 0000000..0cc5a25
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Väljalõikeala all olevate rakenduste renderdamine"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..15d7d60
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Errendatu mozketa-eremutik kanpo geratzen diren aplikazioak"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 0000000..0865f75
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"پرداز زدن برنامه‌ها در زیر ناحیه بریده‌شده"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..1a6bf7a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderöi sovellukset lovialueen alle"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..ea0a27b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Rendre les applications sous la zone de découpe"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 0000000..6d91a9d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Afficher les applis sous la zone d\'encoche"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..382497b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicacións que aparezan na zona recortada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 0000000..d578d92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ઍપને કટઆઉટ ક્ષેત્રની નીચે રેન્ડર કરો"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..e1f09f2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ऐप्लिकेशन को कटआउट एरिया के नीचे दिखाएं"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..db734e8
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderiraj aplikacije ispod područja ureza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 0000000..264095b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Alkalmazások megjelenítése a kivágási terület alatt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 0000000..72e67ec
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Արտապատկերել հավելվածները էկրանի կտրված հատվածի ներքևում"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..c49bf0c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render aplikasi di bawah area potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 0000000..0b90991
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Birta forrit fyrir neðan útklippta svæðið"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..2a0f026b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visualizza le app sotto l\'area di ritaglio"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 0000000..cc7a0a4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"עיבוד האפליקציות שמתחת לאזור המגרעת"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..9e99482
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"カットアウト領域の下でアプリをレンダリング"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..5464a56
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"აპების ასახვა ჭრილის ქვემოთ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 0000000..6a2623f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Экран ойығының астындағы қолданбаларды көрсету"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..4b4d169
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"បំប្លែងកម្មវិធីខាងក្រោមផ្នែកឆក"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 0000000..7a929d1
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ಕಟೌಟ್ ಪ್ರದೇಶದ ಕೆಳಗಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ರೆಂಡರ್ ಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..4b9e640
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"컷아웃 영역 아래에 앱 렌더링"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 0000000..1ac6a8b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Колдонмолорду кесилген аймактын ылдый жагында көрсөтүү"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..4c38580
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ສະແດງພາບແອັບຢູ່ທາງລຸ່ມພື້ນທີ່ຮອຍເສັ້ນ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 0000000..c43736d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Pateikti programas po išpjovos sritimi"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 0000000..f95abb6
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Atveidot lietotnes zem izgriezuma apgabala"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..ff236be
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Прикажувај апликации под отсечената област"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 0000000..ef728ab
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"കട്ടൗട്ട് ഭാഗത്തിന് താഴെ ആപ്പുകൾ റെൻഡർ ചെയ്യുക"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..23dbe0c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Аппуудыг тасалж авсан хэсгийн доор буулгах"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 0000000..42f09cb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट क्षेत्राच्या खाली असलेली ॲप्स रेंडर करा"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..e348630
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Serahkan apl di bawah kawasan potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 0000000..90cb0a5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ဖြတ်ထုတ်ထားသော နေရာအောက်ရှိ အက်ပ်များ ပြသရန်"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 0000000..b8b4e75
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gjengi apper under utklippsområdet"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 0000000..bd213bb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट गरिएको क्षेत्रभन्दा तल पर्ने एपहरू रेन्डर गर्नुहोस्"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..68f5c07
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps renderen onder display-cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 0000000..162a29e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ଆପଗୁଡ଼ିକୁ କଟଆଉଟ୍ ଏରିଆ ନିମ୍ନରେ ରେଣ୍ଡର୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 0000000..908393b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"ਕੱਟਆਊਟ ਖੇਤਰ ਹੇਠ ਐਪਾਂ ਨੂੰ ਰੈਂਡਰ ਕਰੋ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 0000000..c027d52
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderuj aplikacje pod obszarem wycięcia"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..d09ed97
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..d38ce43
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..d09ed97
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..6e5947c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redați aplicațiile sub zona de decupaj"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..c7f54bb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Отображать приложения под вырезом"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 0000000..4a14a36
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"කටවුට් ප්‍රදේශයට පහළින් යෙදුම් විදහන්න"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..98b82e6
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykresľovať aplikácie pod oblasťou výrezu"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 0000000..dcf0c84
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Upodobitev aplikacij pod predelom zareze zaslona"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 0000000..d7b0676
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Paraqiti aplikacionet poshtë zonës së prerjes"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..c2b611e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Приказуј апликације испод области изреза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 0000000..3007ffb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visa appar under skärmutskärningens område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..b605554
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Usionyeshe programu chini ya eneo lenye pengo"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 0000000..c4d06fb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"கட் அவுட் பகுதிக்குள்ளாக ஆப்ஸை ரெண்டர் செய்"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..08fa4ae
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"కట్అవుట్ ఏరియా కింద యాప్‌లను రెండర్ చేయండి"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..9a30250
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"แสดงผลแอปใต้บริเวณรอยบาก"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..a3d4a3a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"I-render ang mga app sa ibaba ng lugar ng cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..12e0f30
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Uygulamaları kesme alanının altında oluşturun"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..08b1521
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Відображати додатки під областю вирізу екрана"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 0000000..711b538
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"کٹ آؤٹ ایریا کے نیچے رینڈر ایپس"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..7f6f2b4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Ekran kesimi quyidagi ilovalarni renderlash"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 0000000..a7d54fb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Hiển thị các ứng dụng bên dưới khu vực có vết cắt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..f596520
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"在刘海区域下方呈现应用"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..ddb1df7
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"在凹口區域下方輸出應用程式"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..7aad79c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"在螢幕凹口底下顯示應用程式畫面"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 0000000..d861c5e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="3814493834951357513">"Nikezela ngama-app angaphansi kwendawo yokukhipha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..b021da7
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Versteek"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..0ca6135
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ደብቅ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 0000000..7e41cb1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"إخفاء"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 0000000..d2399ff
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 0000000..420c513
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizlədin"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 0000000..ce75c45
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Схаваць"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..7c3170c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скриване"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..1e62725
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকান"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 0000000..6ae5ffd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Amaga"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..068bb94
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrýt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..6ecb767
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..44278e8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ausblenden"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..96b1b86
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Απόκρυψη"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..1ab9b2d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..a20e594
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎Hide‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 0000000..4e5428d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Peida"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..f33bb50
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ezkutatu"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 0000000..8de7560
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"پنهان کردن"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..c42e52d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Piilota"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..31fa567
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 0000000..31fa567
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 0000000..7e4b33a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"છુપાવો"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..1513f2a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"छिपाएं"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..082e586
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 0000000..2b9717f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Elrejtés"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 0000000..3407bb4
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Թաքցնել"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..6c017b3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 0000000..229c113
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fela"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..07444aa
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Nascondi"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 0000000..221a129
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"הסתרה"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..3bf17fb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"非表示"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..1600528
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"დამალვა"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 0000000..23d02b8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жасыру"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..624b81f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"លាក់"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 0000000..9cd6da7
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ಮರೆಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..efb6568
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"숨기기"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 0000000..4191325
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жашыруу"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..8850dfb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ເຊື່ອງ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 0000000..6364b96
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Slėpti"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 0000000..61f2ad3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Paslēpt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..505c205
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сокриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 0000000..1c30dec
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"മറയ്ക്കുക"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..7e2719b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Нуух"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 0000000..46f0ab8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"लपवा"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..6c017b3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 0000000..84be3f9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ဝှက်ရန်"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 0000000..6ecb767
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 0000000..ff920b2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाइयोस्"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..00900f8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Verbergen"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 0000000..fcfd725
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ଲୁଚାନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 0000000..9f37e0b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ਲੁਕਾਓ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 0000000..3d65546
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ukryj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..351c1cd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..e6281fd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..9018396
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скрыть"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 0000000..b06a52c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"සඟවන්න"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..965d95b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skryť"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 0000000..e8adb98
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 0000000..85fb95a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fshih"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..26afbf9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 0000000..193c179
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Dölj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..58e35e2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ficha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 0000000..b743c66
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"மறை"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..de04152
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"దాచండి"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..1c8bd9d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"ซ่อน"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..cc45f63
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Itago"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..b20f1f7
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizle"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..938b0e2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сховати"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 0000000..0f08170
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"چھپائیں"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..5d22045
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Berkitish"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 0000000..fc83d25
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ẩn"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..acdfd1c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"隐藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..abb8e81
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..abb8e81
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 0000000..11842d9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fihla"</string>
+</resources>
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index a1c79ef..3109cd8 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -104,7 +104,7 @@
      * @param opt LaunchOptions for clipping
      */
     public void forEachSrc(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
-        blend(1, ain, aout, null);
+        blend(1, ain, aout, opt);
     }
 
     /**
@@ -641,4 +641,3 @@
     }
 */
 }
-
diff --git a/services/Android.bp b/services/Android.bp
index c83a697..a2d6beb4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -21,6 +21,9 @@
             // "-XepPatchLocation:/tmp/refaster/",
         ],
     },
+    lint: {
+        extra_check_modules: ["AndroidFrameworkLintChecker"],
+    },
 }
 
 filegroup {
@@ -130,6 +133,7 @@
     libs: [
         "android.hidl.manager-V1.0-java",
         "framework-tethering.stubs.module_lib",
+        "service-art.stubs.system_server",
     ],
 
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/OWNERS b/services/OWNERS
index 3b972e9..b7128a3 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,6 +1,6 @@
 per-file Android.bp = file:platform/build/soong:/OWNERS
 
 # art-team@ manages the system server profile
-per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
 
 per-file java/com/android/server/* = toddke@google.com,patb@google.com
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e9c9899..eec875a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -21,8 +21,13 @@
 import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
 import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
 import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
+import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -31,6 +36,7 @@
 import android.accessibilityservice.AccessibilityGestureEvent;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
@@ -103,10 +109,9 @@
         FingerprintGestureDispatcher.FingerprintGestureClient {
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
-    private static final String TRACE_A11Y_SERVICE_CONNECTION =
-            LOG_TAG + ".IAccessibilityServiceConnection";
-    private static final String TRACE_A11Y_SERVICE_CLIENT =
-            LOG_TAG + ".IAccessibilityServiceClient";
+    private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection";
+    private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient";
+    private static final String TRACE_WM = "WindowManagerInternal";
     private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
 
     protected static final String TAKE_SCREENSHOT = "takeScreenshot";
@@ -134,6 +139,9 @@
     protected final AccessibilitySecurityPolicy mSecurityPolicy;
     protected final AccessibilityTrace mTrace;
 
+    // The attribution tag set by the service that is bound to this instance
+    protected String mAttributionTag;
+
     // The service that's bound to this instance. Whenever this value is non-null, this
     // object is registered as a death recipient
     IBinder mService;
@@ -298,9 +306,8 @@
             return false;
         }
         try {
-            if (mTrace.isA11yTracingEnabled()) {
-                mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent",
-                        keyEvent + ", " + sequenceNumber);
+            if (svcClientTracingEnabled()) {
+                logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
             }
             mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
         } catch (RemoteException e) {
@@ -365,17 +372,16 @@
 
     @Override
     public void setOnKeyEventResult(boolean handled, int sequence) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult",
-                    "handled=" + handled + ";sequence=" + sequence);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence);
         }
         mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
     }
 
     @Override
     public AccessibilityServiceInfo getServiceInfo() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getServiceInfo", "");
         }
         synchronized (mLock) {
             return mAccessibilityServiceInfo;
@@ -393,8 +399,8 @@
 
     @Override
     public void setServiceInfo(AccessibilityServiceInfo info) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setServiceInfo", "info=" + info);
         }
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -416,13 +422,22 @@
         }
     }
 
+    @Override
+    public void setAttributionTag(String attributionTag) {
+        mAttributionTag = attributionTag;
+    }
+
+    String getAttributionTag() {
+        return mAttributionTag;
+    }
+
     protected abstract boolean hasRightsToCurrentUserLocked();
 
     @Nullable
     @Override
     public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getWindows", "");
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -458,8 +473,8 @@
 
     @Override
     public AccessibilityWindowInfo getWindow(int windowId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getWindow", "windowId=" + windowId);
         }
         synchronized (mLock) {
             int displayId = Display.INVALID_DISPLAY;
@@ -496,8 +511,8 @@
             long accessibilityNodeId, String viewIdResName, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("findAccessibilityNodeInfosByViewId",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
                     + accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
                     + interactionId + ";callback=" + callback + ";interrogatingTid="
@@ -539,6 +554,12 @@
         callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
         final long identityToken = Binder.clearCallingIdentity();
+        if (intConnTracingEnabled()) {
+            logTraceIntConn("findAccessibilityNodeInfosByViewId",
+                    accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
+                    + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+                    + ";" + interrogatingTid + ";" + spec);
+        }
         try {
             connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
                     viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -564,8 +585,8 @@
             long accessibilityNodeId, String text, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("findAccessibilityNodeInfosByText",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
                     + accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
                     + ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
@@ -606,6 +627,12 @@
         callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
         final long identityToken = Binder.clearCallingIdentity();
+        if (intConnTracingEnabled()) {
+            logTraceIntConn("findAccessibilityNodeInfosByText",
+                    accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
+                    + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+                    + ";" + interrogatingTid + ";" + spec);
+        }
         try {
             connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
                     text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -631,13 +658,12 @@
             int accessibilityWindowId, long accessibilityNodeId, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags,
             long interrogatingTid, Bundle arguments) throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(
-                    TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
-                            + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
-                            + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
-                            + ";arguments=" + arguments);
+                    + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+                    + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+                    + ";arguments=" + arguments);
         }
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
@@ -675,6 +701,12 @@
         callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
         final long identityToken = Binder.clearCallingIdentity();
+        if (intConnTracingEnabled()) {
+            logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
+                    accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
+                    + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
+                    + interrogatingTid + ";" + spec + ";" + arguments);
+        }
         try {
             connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
                     accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
@@ -700,12 +732,12 @@
             int focusType, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("findFocus",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
-                            + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
-                            + interactionId + ";callback=" + callback + ";interrogatingTid="
-                            + interrogatingTid);
+                    + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+                    + interactionId + ";callback=" + callback + ";interrogatingTid="
+                    + interrogatingTid);
         }
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
@@ -743,6 +775,12 @@
         callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
         final long identityToken = Binder.clearCallingIdentity();
+        if (intConnTracingEnabled()) {
+            logTraceIntConn("findFocus",
+                    accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
+                    + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+                    + ";" + interrogatingTid + ";" + spec);
+        }
         try {
             connection.getRemote().findFocus(accessibilityNodeId, focusType,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -768,12 +806,12 @@
             int direction, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("focusSearch",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
-                            + accessibilityNodeId + ";direction=" + direction + ";interactionId="
-                            + interactionId + ";callback=" + callback + ";interrogatingTid="
-                            + interrogatingTid);
+                    + accessibilityNodeId + ";direction=" + direction + ";interactionId="
+                    + interactionId + ";callback=" + callback + ";interrogatingTid="
+                    + interrogatingTid);
         }
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
@@ -810,6 +848,12 @@
         callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
         final long identityToken = Binder.clearCallingIdentity();
+        if (intConnTracingEnabled()) {
+            logTraceIntConn("focusSearch",
+                    accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
+                    + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
+                    + interrogatingPid + ";" + interrogatingTid + ";" + spec);
+        }
         try {
             connection.getRemote().focusSearch(accessibilityNodeId, direction,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -832,17 +876,17 @@
 
     @Override
     public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture",
-                    "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn(
+                    "sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
         }
     }
 
     @Override
     public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence="
-                    + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps="
+                    + gestureSteps + ";displayId=" + displayId);
         }
     }
 
@@ -851,12 +895,12 @@
             long accessibilityNodeId, int action, Bundle arguments, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("performAccessibilityAction",
                     "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
-                            + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
-                            + ";interactionId=" + interactionId + ";callback=" + callback
-                            + ";interrogatingTid=" + interrogatingTid);
+                    + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+                    + ";interactionId=" + interactionId + ";callback=" + callback
+                    + ";interrogatingTid=" + interrogatingTid);
         }
         final int resolvedWindowId;
         synchronized (mLock) {
@@ -879,9 +923,8 @@
 
     @Override
     public boolean performGlobalAction(int action) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction",
-                    "action=" + action);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("performGlobalAction", "action=" + action);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -893,8 +936,8 @@
 
     @Override
     public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getSystemActions", "");
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -906,9 +949,8 @@
 
     @Override
     public boolean isFingerprintGestureDetectionAvailable() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(
-                    TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("isFingerprintGestureDetectionAvailable", "");
         }
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
             return false;
@@ -923,9 +965,8 @@
 
     @Override
     public float getMagnificationScale(int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale",
-                    "displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getMagnificationScale", "displayId=" + displayId);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -942,9 +983,8 @@
 
     @Override
     public Region getMagnificationRegion(int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion",
-                    "displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId);
         }
         synchronized (mLock) {
             final Region region = Region.obtain();
@@ -970,9 +1010,8 @@
 
     @Override
     public float getMagnificationCenterX(int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX",
-                    "displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -996,9 +1035,8 @@
 
     @Override
     public float getMagnificationCenterY(int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY",
-                    "displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -1032,9 +1070,8 @@
 
     @Override
     public boolean resetMagnification(int displayId, boolean animate) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification",
-                    "displayId=" + displayId + ";animate=" + animate);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -1058,10 +1095,10 @@
     @Override
     public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
             float centerY, boolean animate) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setMagnificationScaleAndCenter",
                     "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
-                            + ";centerY=" + centerY + ";animate=" + animate);
+                    + ";centerY=" + centerY + ";animate=" + animate);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -1087,8 +1124,8 @@
 
     @Override
     public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setMagnificationCallbackEnabled",
                     "displayId=" + displayId + ";enabled=" + enabled);
         }
         mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
@@ -1100,18 +1137,16 @@
 
     @Override
     public void setSoftKeyboardCallbackEnabled(boolean enabled) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled",
-                    "enabled=" + enabled);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled);
         }
         mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
     }
 
     @Override
     public void takeScreenshot(int displayId, RemoteCallback callback) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot",
-                    "displayId=" + displayId + ";callback=" + callback);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback);
         }
         final long currentTimestamp = SystemClock.uptimeMillis();
         if (mRequestTakeScreenshotTimestampMs != 0
@@ -1237,6 +1272,10 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             final IBinder overlayWindowToken = new Binder();
+            if (wmTracingEnabled()) {
+                logTraceWM("addWindowToken",
+                        overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null");
+            }
             mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
                     displayId, null /* options */);
             synchronized (mLock) {
@@ -1263,6 +1302,10 @@
      */
     public void onDisplayRemoved(int displayId) {
         final long identity = Binder.clearCallingIdentity();
+        if (wmTracingEnabled()) {
+            logTraceWM(
+                    "addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId);
+        }
         try {
             mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true,
                     displayId);
@@ -1282,9 +1325,8 @@
      */
     @Override
     public IBinder getOverlayWindowToken(int displayId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken",
-                    "displayId=" + displayId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId);
         }
         synchronized (mLock) {
             return mOverlayWindowTokens.get(displayId);
@@ -1299,9 +1341,8 @@
      */
     @Override
     public int getWindowIdForLeashToken(@NonNull IBinder token) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken",
-                    "token=" + token);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getWindowIdForLeashToken", "token=" + token);
         }
         synchronized (mLock) {
             return mA11yWindowManager.getWindowIdLocked(token);
@@ -1314,8 +1355,8 @@
             // Clear the proxy in the other process so this
             // IAccessibilityServiceConnection can be garbage collected.
             if (mServiceInterface != null) {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null");
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("init", "null, " + mId + ", null");
                 }
                 mServiceInterface.init(null, mId, null);
             }
@@ -1465,9 +1506,8 @@
         }
 
         try {
-            if (mTrace.isA11yTracingEnabled()) {
-                mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent",
-                        event + ";" + serviceWantsEvent);
+            if (svcClientTracingEnabled()) {
+                logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
             }
             listener.onAccessibilityEvent(event, serviceWantsEvent);
             if (DEBUG) {
@@ -1522,9 +1562,9 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId
-                            + ", " + region + ", " + scale + ", " + centerX + ", " + centerY);
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
+                            + scale + ", " + centerX + ", " + centerY);
                 }
                 listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
             } catch (RemoteException re) {
@@ -1541,9 +1581,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged",
-                            String.valueOf(showState));
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
                 }
                 listener.onSoftKeyboardShowModeChanged(showState);
             } catch (RemoteException re) {
@@ -1557,9 +1596,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked",
-                            String.valueOf(displayId));
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
                 }
                 listener.onAccessibilityButtonClicked(displayId);
             } catch (RemoteException re) {
@@ -1579,9 +1617,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(
-                            TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged",
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
                             String.valueOf(available));
                 }
                 listener.onAccessibilityButtonAvailabilityChanged(available);
@@ -1597,9 +1634,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture",
-                            gestureInfo.toString());
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onGesture", gestureInfo.toString());
                 }
                 listener.onGesture(gestureInfo);
             } catch (RemoteException re) {
@@ -1613,8 +1649,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged");
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onSystemActionsChanged", "");
                 }
                 listener.onSystemActionsChanged();
             } catch (RemoteException re) {
@@ -1628,8 +1664,8 @@
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache");
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("clearAccessibilityCache", "");
                 }
                 listener.clearAccessibilityCache();
             } catch (RemoteException re) {
@@ -1747,6 +1783,12 @@
                 LocalServices.getService(ActivityTaskManagerInternal.class)
                         .setFocusedActivity(activityToken);
             }
+            if (intConnTracingEnabled()) {
+                logTraceIntConn("performAccessibilityAction",
+                        accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId
+                        + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";"
+                        + interrogatingTid);
+            }
             connection.getRemote().performAccessibilityAction(accessibilityNodeId, action,
                     arguments, interactionId, callback, fetchFlags, interrogatingPid,
                     interrogatingTid);
@@ -1957,8 +1999,8 @@
 
     @Override
     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setGestureDetectionPassthroughRegion",
                     "displayId=" + displayId + ";region=" + region);
         }
         mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
@@ -1966,8 +2008,8 @@
 
     @Override
     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion",
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setTouchExplorationPassthroughRegion",
                     "displayId=" + displayId + ";region=" + region);
         }
         mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
@@ -1975,20 +2017,56 @@
 
     @Override
     public void setFocusAppearance(int strokeWidth, int color) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance",
-                    "strokeWidth=" + strokeWidth + ";color=" + color);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color);
         }
     }
 
     @Override
-    public void logTrace(long timestamp, String where, String callingParams, int processId,
-            long threadId, int callingUid, Bundle callingStack) {
-        if (mTrace.isA11yTracingEnabled()) {
+    public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+            int processId, long threadId, int callingUid, Bundle callingStack) {
+        if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
             ArrayList<StackTraceElement> list =
                     (ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK);
-            mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid,
-                    list.toArray(new StackTraceElement[list.size()]));
+            HashSet<String> ignoreList =
+                    (HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK);
+            mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId,
+                    callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList);
         }
     }
+
+    protected boolean svcClientTracingEnabled() {
+        return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
+    }
+
+    protected void logTraceSvcClient(String methodName, String params) {
+        mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName,
+                    FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params);
+    }
+
+    protected boolean svcConnTracingEnabled() {
+        return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION);
+    }
+
+    protected void logTraceSvcConn(String methodName, String params) {
+        mTrace.logTrace(TRACE_SVC_CONN + "." + methodName,
+                FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params);
+    }
+
+    protected boolean intConnTracingEnabled() {
+        return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+    }
+
+    protected void logTraceIntConn(String methodName, String params) {
+        mTrace.logTrace(LOG_TAG + "." + methodName,
+                FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params);
+    }
+
+    protected boolean wmTracingEnabled() {
+        return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+    }
+
+    protected void logTraceWM(String methodName, String params) {
+        mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7403af7..7d2b71f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,7 +19,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
 
+import android.accessibilityservice.AccessibilityTrace;
 import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Region;
 import android.os.PowerManager;
@@ -43,7 +45,10 @@
 import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
 import com.android.server.policy.WindowManagerPolicy;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.StringJoiner;
 
 /**
  * This class is an input filter for implementing accessibility features such
@@ -171,9 +176,9 @@
 
     private int mEnabledFeatures;
 
-    private EventStreamState mMouseStreamState;
+    private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
 
-    private EventStreamState mTouchScreenStreamState;
+    private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
 
     private EventStreamState mKeyboardStreamState;
 
@@ -211,10 +216,17 @@
         super.onUninstalled();
     }
 
-    void onDisplayChanged() {
+    void onDisplayAdded(@NonNull Display display) {
         if (mInstalled) {
-            disableFeatures();
-            enableFeatures();
+            resetStreamStateForDisplay(display.getDisplayId());
+            enableFeaturesForDisplay(display);
+        }
+    }
+
+    void onDisplayRemoved(int displayId) {
+        if (mInstalled) {
+            disableFeaturesForDisplay(displayId);
+            resetStreamStateForDisplay(displayId);
         }
     }
 
@@ -224,7 +236,12 @@
             Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
                     + Integer.toHexString(policyFlags));
         }
-
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
+                AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+            mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
+                    AccessibilityTrace.FLAGS_INPUT_FILTER,
+                    "event=" + event + ";policyFlags=" + policyFlags);
+        }
         if (mEventHandler.size() == 0) {
             if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
             super.onInputEvent(event, policyFlags);
@@ -237,16 +254,17 @@
             return;
         }
 
-        int eventSource = event.getSource();
+        final int eventSource = event.getSource();
+        final int displayId = event.getDisplayId();
         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
             state.reset();
-            clearEventsForAllEventHandlers(eventSource);
+            clearEventStreamHandler(displayId, eventSource);
             super.onInputEvent(event, policyFlags);
             return;
         }
 
         if (state.updateInputSource(event.getSource())) {
-            clearEventsForAllEventHandlers(eventSource);
+            clearEventStreamHandler(displayId, eventSource);
         }
 
         if (!state.inputSourceValid()) {
@@ -275,35 +293,39 @@
      */
     private EventStreamState getEventStreamState(InputEvent event) {
         if (event instanceof MotionEvent) {
-          if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
-              if (mTouchScreenStreamState == null) {
-                  mTouchScreenStreamState = new TouchScreenEventStreamState();
-              }
-              return mTouchScreenStreamState;
-          }
-          if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
-              if (mMouseStreamState == null) {
-                  mMouseStreamState = new MouseEventStreamState();
-              }
-              return mMouseStreamState;
-          }
+            final int displayId = event.getDisplayId();
+
+            if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+                EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+                if (touchScreenStreamState == null) {
+                    touchScreenStreamState = new TouchScreenEventStreamState();
+                    mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
+                }
+                return touchScreenStreamState;
+            }
+            if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+                EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+                if (mouseStreamState == null) {
+                    mouseStreamState = new MouseEventStreamState();
+                    mMouseStreamStates.put(displayId, mouseStreamState);
+                }
+                return mouseStreamState;
+            }
         } else if (event instanceof KeyEvent) {
-          if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
-              if (mKeyboardStreamState == null) {
-                  mKeyboardStreamState = new KeyboardEventStreamState();
-              }
-              return mKeyboardStreamState;
-          }
+            if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+                if (mKeyboardStreamState == null) {
+                    mKeyboardStreamState = new KeyboardEventStreamState();
+                }
+                return mKeyboardStreamState;
+            }
         }
         return null;
     }
 
-    private void clearEventsForAllEventHandlers(int eventSource) {
-        for (int i = 0; i < mEventHandler.size(); i++) {
-            final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
-            if (eventHandler != null) {
-                eventHandler.clearEvents(eventSource);
-            }
+    private void clearEventStreamHandler(int displayId, int eventSource) {
+        final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
+        if (eventHandler != null) {
+            eventHandler.clearEvents(eventSource);
         }
     }
 
@@ -419,55 +441,69 @@
     private void enableFeatures() {
         if (DEBUG) Slog.i(TAG, "enableFeatures()");
 
-        resetStreamState();
+        resetAllStreamState();
 
         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
 
-        if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
-            mAutoclickController = new AutoclickController(mContext, mUserId);
-            addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
-        }
-
         for (int i = displaysList.size() - 1; i >= 0; i--) {
-            final int displayId = displaysList.get(i).getDisplayId();
-            final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
+            enableFeaturesForDisplay(displaysList.get(i));
+        }
+        enableDisplayIndependentFeatures();
+    }
 
-            if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
-                TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
-                if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
-                    explorer.setServiceHandlesDoubleTap(true);
-                }
-                if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
-                    explorer.setMultiFingerGesturesEnabled(true);
-                }
-                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);
-            }
-
-            if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
-                    || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
-                    || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
-                final MagnificationGestureHandler magnificationGestureHandler =
-                        createMagnificationGestureHandler(displayId,
-                                displayContext);
-                addFirstEventHandler(displayId, magnificationGestureHandler);
-                mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
-            }
-
-            if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
-                MotionEventInjector injector = new MotionEventInjector(
-                        mContext.getMainLooper());
-                addFirstEventHandler(displayId, injector);
-                mMotionEventInjectors.put(displayId, injector);
-            }
+    private void enableFeaturesForDisplay(Display display) {
+        if (DEBUG) {
+            Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
         }
 
+        final Context displayContext = mContext.createDisplayContext(display);
+        final int displayId = display.getDisplayId();
+
+        if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
+            if (mAutoclickController == null) {
+                mAutoclickController = new AutoclickController(
+                        mContext, mUserId, mAms.getTraceManager());
+            }
+            addFirstEventHandler(displayId, mAutoclickController);
+        }
+
+        if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+            TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
+            if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
+                explorer.setServiceHandlesDoubleTap(true);
+            }
+            if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
+                explorer.setMultiFingerGesturesEnabled(true);
+            }
+            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);
+        }
+
+        if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+                || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+                || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+            final MagnificationGestureHandler magnificationGestureHandler =
+                    createMagnificationGestureHandler(displayId,
+                            displayContext);
+            addFirstEventHandler(displayId, magnificationGestureHandler);
+            mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+        }
+
+        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+            MotionEventInjector injector = new MotionEventInjector(
+                    mContext.getMainLooper(), mAms.getTraceManager());
+            addFirstEventHandler(displayId, injector);
+            mMotionEventInjectors.put(displayId, injector);
+        }
+    }
+
+    private void enableDisplayIndependentFeatures() {
         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
             mAms.setMotionEventInjectors(mMotionEventInjectors);
         }
@@ -500,55 +536,57 @@
         mEventHandler.put(displayId, eventHandler);
     }
 
-    /**
-     * Adds an event handler to the event handler chain for all displays. The handler is added at
-     * the beginning of the chain.
-     *
-     * @param displayList The list of displays
-     * @param handler The handler to be added to the event handlers list.
-     */
-    private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
-            EventStreamTransformation handler) {
-        for (int i = 0; i < displayList.size(); i++) {
-            final int displayId = displayList.get(i).getDisplayId();
-            addFirstEventHandler(displayId, handler);
+    private void disableFeatures() {
+        final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+        for (int i = displaysList.size() - 1; i >= 0; i--) {
+            disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
+        }
+        mAms.setMotionEventInjectors(null);
+        disableDisplayIndependentFeatures();
+
+        resetAllStreamState();
+    }
+
+    private void disableFeaturesForDisplay(int displayId) {
+        if (DEBUG) {
+            Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
+        }
+
+        final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
+        if (injector != null) {
+            injector.onDestroy();
+            mMotionEventInjectors.remove(displayId);
+        }
+
+        final TouchExplorer explorer = mTouchExplorer.get(displayId);
+        if (explorer != null) {
+            explorer.onDestroy();
+            mTouchExplorer.remove(displayId);
+        }
+
+        final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
+        if (handler != null) {
+            handler.onDestroy();
+            mMagnificationGestureHandler.remove(displayId);
+        }
+
+        final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
+        if (eventStreamTransformation != null) {
+            mEventHandler.remove(displayId);
         }
     }
 
-    private void disableFeatures() {
-        for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
-            final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
-            if (injector != null) {
-                injector.onDestroy();
-            }
-        }
-        mAms.setMotionEventInjectors(null);
-        mMotionEventInjectors.clear();
+    private void disableDisplayIndependentFeatures() {
         if (mAutoclickController != null) {
             mAutoclickController.onDestroy();
             mAutoclickController = null;
         }
-        for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
-            final TouchExplorer explorer = mTouchExplorer.valueAt(i);
-            if (explorer != null) {
-                explorer.onDestroy();
-            }
-        }
-        mTouchExplorer.clear();
-        for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
-            final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
-            if (handler != null) {
-                handler.onDestroy();
-            }
-        }
-        mMagnificationGestureHandler.clear();
+
         if (mKeyboardInterceptor != null) {
             mKeyboardInterceptor.onDestroy();
             mKeyboardInterceptor = null;
         }
-
-        mEventHandler.clear();
-        resetStreamState();
     }
 
     private MagnificationGestureHandler createMagnificationGestureHandler(
@@ -563,32 +601,46 @@
             final Context uiContext = displayContext.createWindowContext(
                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
             magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
-                    mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
-                    detectControlGestures, triggerable,
+                    mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+                    mAms.getMagnificationController(), detectControlGestures, triggerable,
                     displayId);
         } else {
             final Context uiContext = displayContext.createWindowContext(
                     TYPE_MAGNIFICATION_OVERLAY, null /* options */);
             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
-                    mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
-                    detectControlGestures, triggerable,
+                    mAms.getFullScreenMagnificationController(), mAms.getTraceManager(),
+                    mAms.getMagnificationController(), detectControlGestures, triggerable,
                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
         }
         return magnificationGestureHandler;
     }
 
-    void resetStreamState() {
-        if (mTouchScreenStreamState != null) {
-            mTouchScreenStreamState.reset();
+    void resetAllStreamState() {
+        final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+        for (int i = displaysList.size() - 1; i >= 0; i--) {
+            resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
         }
-        if (mMouseStreamState != null) {
-            mMouseStreamState.reset();
-        }
+
         if (mKeyboardStreamState != null) {
             mKeyboardStreamState.reset();
         }
     }
 
+    void resetStreamStateForDisplay(int displayId) {
+        final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+        if (touchScreenStreamState != null) {
+            touchScreenStreamState.reset();
+            mTouchScreenStreamStates.remove(displayId);
+        }
+
+        final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+        if (mouseStreamState != null) {
+            mouseStreamState.reset();
+            mMouseStreamStates.remove(displayId);
+        }
+    }
+
     @Override
     public void onDestroy() {
         /* ignore */
@@ -839,4 +891,45 @@
             mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
         }
     }
+
+    /**
+     * Dumps all {@link AccessibilityInputFilter}s here.
+     */
+    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+        if (mEventHandler == null) {
+            return;
+        }
+        pw.append("A11yInputFilter Info : ");
+        pw.println();
+
+        final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+        for (int i = 0; i < displaysList.size(); i++) {
+            final int displayId = displaysList.get(i).getDisplayId();
+            EventStreamTransformation next = mEventHandler.get(displayId);
+            if (next != null) {
+                pw.append("Enabled features of Display [");
+                pw.append(Integer.toString(displayId));
+                pw.append("] = ");
+
+                final StringJoiner joiner = new StringJoiner(",", "[", "]");
+
+                while (next != null) {
+                    if (next instanceof MagnificationGestureHandler) {
+                        joiner.add("MagnificationGesture");
+                    } else if (next instanceof KeyboardInterceptor) {
+                        joiner.add("KeyboardInterceptor");
+                    } else if (next instanceof TouchExplorer) {
+                        joiner.add("TouchExplorer");
+                    } else if (next instanceof AutoclickController) {
+                        joiner.add("AutoclickController");
+                    } else if (next instanceof MotionEventInjector) {
+                        joiner.add("MotionEventInjector");
+                    }
+                    next = next.getNext();
+                }
+                pw.append(joiner.toString());
+            }
+            pw.println();
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f631988..7b2b8e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,15 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -289,8 +298,8 @@
         mContext = context;
         mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
-        mTraceManager = new AccessibilityTraceManager(
-                mWindowManagerService.getAccessibilityController(), this);
+        mTraceManager = AccessibilityTraceManager.getInstance(
+                mWindowManagerService.getAccessibilityController(), this, mLock);
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
         mPackageManager = packageManager;
@@ -311,8 +320,8 @@
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
-        mTraceManager = new AccessibilityTraceManager(
-                mWindowManagerService.getAccessibilityController(), this);
+        mTraceManager = AccessibilityTraceManager.getInstance(
+                mWindowManagerService.getAccessibilityController(), this, mLock);
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
         mPackageManager = mContext.getPackageManager();
@@ -324,7 +333,7 @@
         mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
                 this);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
-                mWindowManagerService, this, mSecurityPolicy, this);
+                mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
         mMagnificationController = new MagnificationController(this, mLock, mContext);
         init();
@@ -339,26 +348,16 @@
 
     @Override
     public int getCurrentUserIdLocked() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked");
-        }
         return mCurrentUserId;
     }
 
     @Override
     public boolean isAccessibilityButtonShown() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown");
-        }
         return mIsAccessibilityButtonShown;
     }
 
     @Override
     public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(
-                    LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
-        }
         mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
                 userState.mBoundServices);
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -424,8 +423,9 @@
         PackageMonitor monitor = new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                if (mTraceManager.isA11yTracingEnabled()) {
-                    mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+                    mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
+                            FLAGS_PACKAGE_BROADCAST_RECEIVER);
                 }
 
                 synchronized (mLock) {
@@ -452,8 +452,9 @@
                 // mBindingServices in binderDied() during updating. Remove services from  this
                 // package from mBindingServices, and then update the user state to re-bind new
                 // versions of them.
-                if (mTraceManager.isA11yTracingEnabled()) {
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
                     mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+                            FLAGS_PACKAGE_BROADCAST_RECEIVER,
                             "packageName=" + packageName + ";uid=" + uid);
                 }
                 synchronized (mLock) {
@@ -485,8 +486,9 @@
 
             @Override
             public void onPackageRemoved(String packageName, int uid) {
-                if (mTraceManager.isA11yTracingEnabled()) {
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
                     mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
+                            FLAGS_PACKAGE_BROADCAST_RECEIVER,
                             "packageName=" + packageName + ";uid=" + uid);
                 }
 
@@ -529,8 +531,9 @@
             @Override
             public boolean onHandleForceStop(Intent intent, String[] packages,
                     int uid, boolean doit) {
-                if (mTraceManager.isA11yTracingEnabled()) {
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
                     mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+                            FLAGS_PACKAGE_BROADCAST_RECEIVER,
                             "intent=" + intent + ";packages=" + packages + ";uid=" + uid
                             + ";doit=" + doit);
                 }
@@ -580,8 +583,8 @@
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                if (mTraceManager.isA11yTracingEnabled()) {
-                    mTraceManager.logTrace(LOG_TAG + ".BR.onReceive",
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) {
+                    mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER,
                             "context=" + context + ";intent=" + intent);
                 }
 
@@ -668,8 +671,8 @@
 
     @Override
     public long addClient(IAccessibilityManagerClient callback, int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".addClient",
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".addClient", FLAGS_ACCESSIBILITY_MANAGER,
                     "callback=" + callback + ";userId=" + userId);
         }
 
@@ -739,11 +742,12 @@
 
     @Override
     public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER,
                     "event=" + event + ";userId=" + userId);
         }
         boolean dispatchEvent = false;
+        int resolvedUserId;
 
         synchronized (mLock) {
             if (event.getWindowId() ==
@@ -759,8 +763,7 @@
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
             // performs the current profile parent resolution.
-            final int resolvedUserId = mSecurityPolicy
-                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+            resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
             // Make sure the reported package is one the caller has access to.
             event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
@@ -792,17 +795,23 @@
             int displayId = Display.INVALID_DISPLAY;
             synchronized (mLock) {
                 final int windowId = event.getWindowId();
-                if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-                        && windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+                if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
                     displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
-                            mCurrentUserId, windowId);
+                            resolvedUserId, windowId);
+                    event.setDisplayId(displayId);
                 }
-                if (displayId != Display.INVALID_DISPLAY
+
+                if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                        && displayId != Display.INVALID_DISPLAY
                         && mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
                     shouldComputeWindows = true;
                 }
             }
             if (shouldComputeWindows) {
+                if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+                    mTraceManager.logTrace("WindowManagerInternal.computeWindowsForAccessibility",
+                            FLAGS_WINDOW_MANAGER_INTERNAL, "display=" + displayId);
+                }
                 final WindowManagerInternal wm = LocalServices.getService(
                         WindowManagerInternal.class);
                 wm.computeWindowsForAccessibility(displayId);
@@ -835,9 +844,9 @@
      */
     @Override
     public void registerSystemAction(RemoteAction action, int actionId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
-                    "action=" + action + ";actionId=" + actionId);
+                    FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId);
         }
         mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
         getSystemActionPerformer().registerSystemAction(actionId, action);
@@ -850,8 +859,9 @@
      */
     @Override
     public void unregisterSystemAction(int actionId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction",
+                    FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId);
         }
         mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
         getSystemActionPerformer().unregisterSystemAction(actionId);
@@ -867,9 +877,9 @@
 
     @Override
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList",
-                    "userId=" + userId);
+                    FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
         }
 
         final int resolvedUserId;
@@ -903,8 +913,9 @@
     @Override
     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
             int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+                    FLAGS_ACCESSIBILITY_MANAGER,
                     "feedbackType=" + feedbackType + ";userId=" + userId);
         }
 
@@ -936,8 +947,9 @@
 
     @Override
     public void interrupt(int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".interrupt",
+                    FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
         }
 
         List<IAccessibilityServiceClient> interfacesToInterrupt;
@@ -966,8 +978,10 @@
         }
         for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
             try {
-                if (mTraceManager.isA11yTracingEnabled()) {
-                    mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+                if (mTraceManager.isA11yTracingEnabledForTypes(
+                        FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+                    mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt",
+                            FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
                 }
                 interfacesToInterrupt.get(i).onInterrupt();
             } catch (RemoteException re) {
@@ -981,8 +995,9 @@
     public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
             IAccessibilityInteractionConnection connection, String packageName,
             int userId) throws RemoteException {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+                    FLAGS_ACCESSIBILITY_MANAGER,
                     "windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
                             + connection + "; packageName=" + packageName + ";userId=" + userId);
         }
@@ -993,9 +1008,9 @@
 
     @Override
     public void removeAccessibilityInteractionConnection(IWindow window) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection",
-                    "window=" + window);
+                    FLAGS_ACCESSIBILITY_MANAGER, "window=" + window);
         }
         mA11yWindowManager.removeAccessibilityInteractionConnection(window);
     }
@@ -1003,9 +1018,9 @@
     @Override
     public void setPictureInPictureActionReplacingConnection(
             IAccessibilityInteractionConnection connection) throws RemoteException {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
-                    "connection=" + connection);
+                    FLAGS_ACCESSIBILITY_MANAGER, "connection=" + connection);
         }
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
                 SET_PIP_ACTION_REPLACEMENT);
@@ -1017,10 +1032,11 @@
             IAccessibilityServiceClient serviceClient,
             AccessibilityServiceInfo accessibilityServiceInfo,
             int flags) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
-                    + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
-                    + accessibilityServiceInfo + ";flags=" + flags);
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService",
+                    FLAGS_ACCESSIBILITY_MANAGER,
+                    "owner=" + owner + ";serviceClient=" + serviceClient
+                    + ";accessibilityServiceInfo=" + accessibilityServiceInfo + ";flags=" + flags);
         }
 
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
@@ -1037,9 +1053,9 @@
 
     @Override
     public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
-                    "serviceClient=" + serviceClient);
+                    FLAGS_ACCESSIBILITY_MANAGER, "serviceClient=" + serviceClient);
         }
         synchronized (mLock) {
             mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
@@ -1049,15 +1065,20 @@
     @Override
     public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
             ComponentName service, boolean touchExplorationEnabled) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(
                     LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+                    FLAGS_ACCESSIBILITY_MANAGER,
                     "service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
         }
 
         mSecurityPolicy.enforceCallingPermission(
                 Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
                 TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+            mTraceManager.logTrace("WindowManagerInternal.isKeyguardLocked",
+                    FLAGS_WINDOW_MANAGER_INTERNAL);
+        }
         if (!mWindowManagerService.isKeyguardLocked()) {
             return;
         }
@@ -1083,9 +1104,9 @@
 
     @Override
     public IBinder getWindowToken(int windowId, int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getWindowToken",
-                    "windowId=" + windowId + ";userId=" + userId);
+                    FLAGS_ACCESSIBILITY_MANAGER, "windowId=" + windowId + ";userId=" + userId);
         }
 
         mSecurityPolicy.enforceCallingPermission(
@@ -1127,8 +1148,9 @@
      */
     @Override
     public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+                    FLAGS_ACCESSIBILITY_MANAGER,
                     "displayId=" + displayId + ";targetName=" + targetName);
         }
 
@@ -1157,9 +1179,9 @@
      */
     @Override
     public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
-                    "shown=" + shown);
+                    FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown);
         }
 
         mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -1190,10 +1212,6 @@
      */
     @Override
     public void onSystemActionsChanged() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
-        }
-
         synchronized (mLock) {
             AccessibilityUserState state = getCurrentUserStateLocked();
             notifySystemActionsChangedLocked(state);
@@ -1256,11 +1274,6 @@
 
     @Override
     public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked",
-                    "displayId=" + displayId);
-        }
-
         final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
         MotionEventInjector motionEventInjector = null;
         while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1323,6 +1336,10 @@
         synchronized (mLock) {
             token = getWindowToken(windowId, mCurrentUserId);
         }
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+            mTraceManager.logTrace("WindowManagerInternal.getWindowFrame",
+                    FLAGS_WINDOW_MANAGER_INTERNAL, "token=" + token + ";outBounds=" + outBounds);
+        }
         mWindowManagerService.getWindowFrame(token, outBounds);
         if (!outBounds.isEmpty()) {
             return true;
@@ -1471,7 +1488,7 @@
     private int getClientStateLocked(AccessibilityUserState userState) {
         return userState.getClientStateLocked(
             mUiAutomationManager.isUiAutomationRunningLocked(),
-            mTraceManager.isA11yTracingEnabled());
+            mTraceManager.getTraceStateForAccessibilityManagerClientState());
     }
 
     private InteractionBridge getInteractionBridge() {
@@ -1681,6 +1698,10 @@
     }
 
     private void updateRelevantEventsLocked(AccessibilityUserState userState) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+            mTraceManager.logTrace(LOG_TAG + ".updateRelevantEventsLocked",
+                    FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+        }
         mMainHandler.post(() -> {
             broadcastToClients(userState, ignoreRemoteException(client -> {
                 int relevantEventTypes;
@@ -1830,12 +1851,6 @@
     @Override
     public void persistComponentNamesToSettingLocked(String settingName,
             Set<ComponentName> componentNames, int userId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked",
-                    "settingName=" + settingName + ";componentNames=" + componentNames + ";userId="
-                            + userId);
-        }
-
         persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
                 componentName -> componentName.flattenToShortString());
     }
@@ -1960,7 +1975,7 @@
         }
     }
 
-    private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+    void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
         final int clientState = getClientStateLocked(userState);
         if (userState.getLastSentClientStateLocked() != clientState
                 && (mGlobalClients.getRegisteredCallbackCount() > 0
@@ -1983,6 +1998,10 @@
 
     private void sendStateToClients(int clientState,
             RemoteCallbackList<IAccessibilityManagerClient> clients) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+            mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
+                    FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
+        }
         clients.broadcast(ignoreRemoteException(
                 client -> client.setState(clientState)));
     }
@@ -2003,6 +2022,10 @@
 
     private void notifyClientsOfServicesStateChange(
             RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+            mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
+                    FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
+        }
         clients.broadcast(ignoreRemoteException(
                 client -> client.notifyServicesStateChanged(uiTimeout)));
     }
@@ -2082,6 +2105,12 @@
             }
         }
         if (setInputFilter) {
+            if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL
+                    | FLAGS_INPUT_FILTER)) {
+                mTraceManager.logTrace("WindowManagerInternal.setInputFilter",
+                        FLAGS_WINDOW_MANAGER_INTERNAL | FLAGS_INPUT_FILTER,
+                        "inputFilter=" + inputFilter);
+            }
             mWindowManagerService.setInputFilter(inputFilter);
         }
     }
@@ -2805,26 +2834,21 @@
     @GuardedBy("mLock")
     @Override
     public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked",
-                    "windowId=" + windowId);
-        }
-
         IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
                 mCurrentUserId, windowId);
         if (windowToken != null) {
-            return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
-                    windowToken);
+            if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+                mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow",
+                        FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken);
+            }
+
+            return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken);
         }
         return null;
     }
 
     @Override
     public KeyEventDispatcher getKeyEventDispatcher() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher");
-        }
-
         if (mKeyEventDispatcher == null) {
             mKeyEventDispatcher = new KeyEventDispatcher(
                     mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
@@ -2837,13 +2861,6 @@
     @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
             int flags) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity",
-                    "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent
-                            + ";flags=" + flags);
-        }
-
-
         return PendingIntent.getActivity(context, requestCode, intent, flags);
     }
 
@@ -2858,9 +2875,9 @@
      */
     @Override
     public void performAccessibilityShortcut(String targetName) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
-                    "targetName=" + targetName);
+                    FLAGS_ACCESSIBILITY_MANAGER, "targetName=" + targetName);
         }
 
         if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
@@ -3048,9 +3065,9 @@
 
     @Override
     public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets",
-                    "shortcutType=" + shortcutType);
+                    FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType);
         }
 
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@@ -3122,11 +3139,6 @@
 
     @Override
     public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
-                    "event=" + event);
-        }
-
         sendAccessibilityEventLocked(event, mCurrentUserId);
     }
 
@@ -3148,8 +3160,10 @@
      */
     @Override
     public boolean sendFingerprintGesture(int gestureKeyCode) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(
+                FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT)) {
             mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture",
+                    FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT,
                     "gestureKeyCode=" + gestureKeyCode);
         }
 
@@ -3174,9 +3188,9 @@
      */
     @Override
     public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId",
-                    "windowToken=" + windowToken);
+                    FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken);
         }
 
         synchronized (mLock) {
@@ -3196,8 +3210,9 @@
      */
     @Override
     public long getRecommendedTimeoutMillis() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(
+                    LOG_TAG + ".getRecommendedTimeoutMillis", FLAGS_ACCESSIBILITY_MANAGER);
         }
 
         synchronized(mLock) {
@@ -3214,8 +3229,10 @@
     @Override
     public void setWindowMagnificationConnection(
             IWindowMagnificationConnection connection) throws RemoteException {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(
+                FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
             mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
+                    FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
                     "connection=" + connection);
         }
 
@@ -3249,9 +3266,9 @@
 
     @Override
     public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
-        if (mTraceManager.isA11yTracingEnabled()) {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
-                    "host=" + host + ";embedded=" + embedded);
+                    FLAGS_ACCESSIBILITY_MANAGER, "host=" + host + ";embedded=" + embedded);
         }
 
         synchronized (mLock) {
@@ -3261,8 +3278,9 @@
 
     @Override
     public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy",
+                    FLAGS_ACCESSIBILITY_MANAGER, "token=" + token);
         }
 
         synchronized (mLock) {
@@ -3274,7 +3292,11 @@
      * Gets the stroke width of the focus rectangle.
      * @return The stroke width.
      */
+    @Override
     public int getFocusStrokeWidth() {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
+        }
         synchronized (mLock) {
             final AccessibilityUserState userState = getCurrentUserStateLocked();
 
@@ -3286,7 +3308,11 @@
      * Gets the color of the focus rectangle.
      * @return The color.
      */
+    @Override
     public int getFocusColor() {
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+            mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
+        }
         synchronized (mLock) {
             final AccessibilityUserState userState = getCurrentUserStateLocked();
 
@@ -3314,6 +3340,9 @@
                 pw.println();
             }
             mA11yWindowManager.dump(fd, pw, args);
+            if (mHasInputFilter && mInputFilter != null) {
+                mInputFilter.dump(fd, pw, args);
+            }
             pw.println("Global client list info:{");
             mGlobalClients.dump(pw, "    Client list ");
             pw.println("    Registered clients:{");
@@ -3350,9 +3379,6 @@
 
     @Override
     public FullScreenMagnificationController getFullScreenMagnificationController() {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
-        }
         synchronized (mLock) {
             return mMagnificationController.getFullScreenMagnificationController();
         }
@@ -3360,11 +3386,6 @@
 
     @Override
     public void onClientChangeLocked(boolean serviceInfoChanged) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
-                    "serviceInfoChanged=" + serviceInfoChanged);
-        }
-
         AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
         onUserStateChangedLocked(userState);
         if (serviceInfoChanged) {
@@ -3569,7 +3590,7 @@
             synchronized (mLock) {
                 mDisplaysList.add(display);
                 if (mInputFilter != null) {
-                    mInputFilter.onDisplayChanged();
+                    mInputFilter.onDisplayAdded(display);
                 }
                 AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3591,7 +3612,7 @@
                     return;
                 }
                 if (mInputFilter != null) {
-                    mInputFilter.onDisplayChanged();
+                    mInputFilter.onDisplayRemoved(displayId);
                 }
                 AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3891,11 +3912,6 @@
 
     @Override
     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
-                    "displayId=" + displayId + ";region=" + region);
-        }
-
         mMainHandler.sendMessage(
                 obtainMessage(
                         AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal,
@@ -3906,11 +3922,6 @@
 
     @Override
     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
-        if (mTraceManager.isA11yTracingEnabled()) {
-            mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
-                    "displayId=" + displayId + ";region=" + region);
-        }
-
         mMainHandler.sendMessage(
                 obtainMessage(
                         AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal,
@@ -3939,7 +3950,10 @@
         if (userState.mUserId != mCurrentUserId) {
             return;
         }
-
+        if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+            mTraceManager.logTrace(LOG_TAG + ".updateFocusAppearanceDataLocked",
+                    FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+        }
         mMainHandler.post(() -> {
             broadcastToClients(userState, ignoreRemoteException(client -> {
                 client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
@@ -3949,7 +3963,7 @@
 
     }
 
-    AccessibilityTraceManager getTraceManager() {
+    public AccessibilityTraceManager getTraceManager() {
         return mTraceManager;
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index dc2628f..0ab0c89 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -444,8 +444,10 @@
     private boolean isValidPackageForUid(String packageName, int uid) {
         final long token = Binder.clearCallingIdentity();
         try {
+            // Since we treat calls from a profile as if made by its parent, using
+            // MATCH_ANY_USER to query the uid of the given package name.
             return uid == mPackageManager.getPackageUidAsUser(
-                    packageName, UserHandle.getUserId(uid));
+                    packageName, PackageManager.MATCH_ANY_USER, UserHandle.getUserId(uid));
         } catch (PackageManager.NameNotFoundException e) {
             return false;
         } finally {
@@ -534,7 +536,8 @@
 
         int servicePackageUid = serviceInfo.applicationInfo.uid;
         if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
-                servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
+                servicePackageUid, serviceInfo.packageName, null, null)
+                != AppOpsManager.MODE_ALLOWED) {
             Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
                     serviceInfo.packageName, serviceInfo.name).flattenToShortString()
                     + ": disallowed by AppOps");
@@ -559,17 +562,21 @@
             return true;
         }
 
-        final int uid = resolveInfo.serviceInfo.applicationInfo.uid;
+        final int servicePackageUid = resolveInfo.serviceInfo.applicationInfo.uid;
+        final int callingPid = Binder.getCallingPid();
         final long identityToken = Binder.clearCallingIdentity();
+        final String attributionTag = service.getAttributionTag();
         try {
             // For the caller is system, just block the data to a11y services.
-            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+            if (OWN_PROCESS_ID == callingPid) {
                 return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
-                        uid, packageName) == AppOpsManager.MODE_ALLOWED;
+                        servicePackageUid, packageName, attributionTag, null)
+                        == AppOpsManager.MODE_ALLOWED;
             }
 
             return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
-                    uid, packageName) == AppOpsManager.MODE_ALLOWED;
+                    servicePackageUid, packageName, attributionTag, null)
+                    == AppOpsManager.MODE_ALLOWED;
         } finally {
             Binder.restoreCallingIdentity(identityToken);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 7d75b73..467cab5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -20,6 +20,7 @@
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -53,10 +54,7 @@
  */
 class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
     private static final String LOG_TAG = "AccessibilityServiceConnection";
-    private static final String TRACE_A11Y_SERVICE_CONNECTION =
-            LOG_TAG + ".IAccessibilityServiceConnection";
-    private static final String TRACE_A11Y_SERVICE_CLIENT =
-            LOG_TAG + ".IAccessibilityServiceClient";
+
     /*
      Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
      lists of bound and binding services. These are freed on user changes, but just in case it
@@ -137,8 +135,8 @@
 
     @Override
     public void disableSelf() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("disableSelf", "");
         }
         synchronized (mLock) {
             AccessibilityUserState userState = mUserStateWeakReference.get();
@@ -218,9 +216,9 @@
             return;
         }
         try {
-            if (mTrace.isA11yTracingEnabled()) {
-                mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", "
-                        + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+            if (svcClientTracingEnabled()) {
+                logTraceSvcClient("init",
+                        this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
             }
             serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
         } catch (RemoteException re) {
@@ -264,9 +262,8 @@
 
     @Override
     public boolean setSoftKeyboardShowMode(int showMode) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode",
-                    "showMode=" + showMode);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -280,8 +277,8 @@
 
     @Override
     public int getSoftKeyboardShowMode() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("getSoftKeyboardShowMode", "");
         }
         final AccessibilityUserState userState = mUserStateWeakReference.get();
         return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
@@ -289,9 +286,8 @@
 
     @Override
     public boolean switchToInputMethod(String imeId) {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod",
-                    "imeId=" + imeId);
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -311,8 +307,8 @@
 
     @Override
     public boolean isAccessibilityButtonAvailable() {
-        if (mTrace.isA11yTracingEnabled()) {
-            mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable");
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("isAccessibilityButtonAvailable", "");
         }
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
@@ -373,9 +369,9 @@
         }
         if (serviceInterface != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT
-                            + ".onFingerprintCapturingGesturesChanged", String.valueOf(active));
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient(
+                            "onFingerprintCapturingGesturesChanged", String.valueOf(active));
                 }
                 mServiceInterface.onFingerprintCapturingGesturesChanged(active);
             } catch (RemoteException e) {
@@ -394,9 +390,8 @@
         }
         if (serviceInterface != null) {
             try {
-                if (mTrace.isA11yTracingEnabled()) {
-                    mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture",
-                            String.valueOf(gesture));
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
                 }
                 mServiceInterface.onFingerprintGesture(gesture);
             } catch (RemoteException e) {
@@ -410,15 +405,17 @@
             if (mSecurityPolicy.canPerformGestures(this)) {
                 MotionEventInjector motionEventInjector =
                         mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
+                if (wmTracingEnabled()) {
+                    logTraceWM("isTouchOrFaketouchDevice", "");
+                }
                 if (motionEventInjector != null
                         && mWindowManagerService.isTouchOrFaketouchDevice()) {
                     motionEventInjector.injectEvents(
                             gestureSteps.getList(), mServiceInterface, sequence, displayId);
                 } else {
                     try {
-                        if (mTrace.isA11yTracingEnabled()) {
-                            mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult",
-                                    sequence + ", false");
+                        if (svcClientTracingEnabled()) {
+                            logTraceSvcClient("onPerformGestureResult", sequence + ", false");
                         }
                         mServiceInterface.onPerformGestureResult(sequence, false);
                     } catch (RemoteException re) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 6396960..8cf5547 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -60,7 +60,7 @@
             }
             case "start-trace":
             case "stop-trace":
-                return mService.getTraceManager().onShellCommand(cmd);
+                return mService.getTraceManager().onShellCommand(cmd, this);
         }
         return -1;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
deleted file mode 100644
index 0391413..0000000
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.accessibility;
-
-/**
- * Interface to log accessibility trace.
- */
-public interface AccessibilityTrace {
-    /**
-     * Whether the trace is enabled.
-     */
-    boolean isA11yTracingEnabled();
-
-    /**
-     * Start tracing.
-     */
-    void startTrace();
-
-    /**
-     * Stop tracing.
-     */
-    void stopTrace();
-
-    /**
-     * Log one trace entry.
-     * @param where A string to identify this log entry, which can be used to filter/search
-     *        through the tracing file.
-     */
-    void logTrace(String where);
-
-    /**
-     * Log one trace entry.
-     * @param where A string to identify this log entry, which can be used to filter/search
-     *        through the tracing file.
-     * @param callingParams The parameters for the method to be logged.
-     */
-    void logTrace(String where, String callingParams);
-
-    /**
-     * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
-     * make screen content related requests use this API to log entry when receive callback.
-     * @param timestamp The timestamp when a callback is received.
-     * @param where A string to identify this log entry, which can be used to filter/search
-     *        through the tracing file.
-     * @param callingParams The parameters for the callback.
-     * @param processId The process id of the calling component.
-     * @param threadId The threadId of the calling component.
-     * @param callingUid The calling uid of the callback.
-     * @param callStack The call stack of the callback.
-     */
-    void logTrace(long timestamp, String where, String callingParams, int processId,
-            long threadId, int callingUid, StackTraceElement[] callStack);
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
index 6105e8a..51e01ea 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -15,72 +15,197 @@
  */
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_ALL;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_NONE;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+
+import android.accessibilityservice.AccessibilityTrace;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.os.Binder;
+import android.os.ShellCommand;
 
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Manager of accessibility trace.
  */
-class AccessibilityTraceManager implements AccessibilityTrace {
+public class AccessibilityTraceManager implements AccessibilityTrace {
     private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
     private final AccessibilityManagerService mService;
+    private final Object mA11yMSLock;
 
-    AccessibilityTraceManager(
+    private long mEnabledLoggingFlags;
+
+    private static AccessibilityTraceManager sInstance = null;
+
+    @MainThread
+    static AccessibilityTraceManager getInstance(
             @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
-            @NonNull AccessibilityManagerService service) {
+            @NonNull AccessibilityManagerService service,
+            @NonNull Object lock) {
+        if (sInstance == null) {
+            sInstance = new AccessibilityTraceManager(a11yController, service, lock);
+        }
+        return sInstance;
+    }
+
+    private AccessibilityTraceManager(
+            @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+            @NonNull AccessibilityManagerService service,
+            @NonNull Object lock) {
         mA11yController = a11yController;
         mService = service;
+        mA11yMSLock = lock;
+        mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
     }
 
     @Override
     public boolean isA11yTracingEnabled() {
-        return mA11yController.isAccessibilityTracingEnabled();
+        synchronized (mA11yMSLock) {
+            return mEnabledLoggingFlags != FLAGS_LOGGING_NONE;
+        }
     }
 
     @Override
-    public void startTrace() {
-        if (!mA11yController.isAccessibilityTracingEnabled()) {
-            mA11yController.startTrace();
-            mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+    public boolean isA11yTracingEnabledForTypes(long typeIdFlags) {
+        synchronized (mA11yMSLock) {
+            return ((typeIdFlags & mEnabledLoggingFlags) != FLAGS_LOGGING_NONE);
         }
     }
 
     @Override
+    public int getTraceStateForAccessibilityManagerClientState() {
+        int state = 0x0;
+        synchronized (mA11yMSLock) {
+            if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION)) {
+                state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+            }
+            if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK)) {
+                state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+            }
+            if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CLIENT)) {
+                state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+            }
+            if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE)) {
+                state |= STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+            }
+        }
+        return state;
+    }
+
+    @Override
+    public void startTrace(long loggingTypes) {
+        if (loggingTypes == FLAGS_LOGGING_NONE) {
+            // Ignore start none request
+            return;
+        }
+
+        synchronized (mA11yMSLock) {
+            long oldEnabled = mEnabledLoggingFlags;
+            mEnabledLoggingFlags = loggingTypes;
+
+            if (needToNotifyClients(oldEnabled)) {
+                mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+            }
+        }
+
+        mA11yController.startTrace(loggingTypes);
+    }
+
+    @Override
     public void stopTrace() {
-        if (mA11yController.isAccessibilityTracingEnabled()) {
+        boolean stop = false;
+        synchronized (mA11yMSLock) {
+            stop = isA11yTracingEnabled();
+
+            long oldEnabled = mEnabledLoggingFlags;
+            mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
+
+            if (needToNotifyClients(oldEnabled)) {
+                mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+            }
+        }
+        if (stop) {
             mA11yController.stopTrace();
-            mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
         }
     }
 
     @Override
-    public void logTrace(String where) {
-        logTrace(where, "");
+    public void logTrace(String where, long loggingTypes) {
+        logTrace(where, loggingTypes, "");
     }
 
     @Override
-    public void logTrace(String where, String callingParams) {
-        mA11yController.logTrace(where, callingParams, "".getBytes(),
-                Binder.getCallingUid(), Thread.currentThread().getStackTrace());
-    }
-
-    @Override
-    public void logTrace(long timestamp, String where, String callingParams, int processId,
-            long threadId, int callingUid, StackTraceElement[] callStack) {
-        if (mA11yController.isAccessibilityTracingEnabled()) {
-            mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
-                    timestamp, processId, threadId);
+    public void logTrace(String where, long loggingTypes, String callingParams) {
+        if (isA11yTracingEnabledForTypes(loggingTypes)) {
+            mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(),
+                    Binder.getCallingUid(), Thread.currentThread().getStackTrace(),
+                    new HashSet<String>(Arrays.asList("logTrace")));
         }
     }
 
-    int onShellCommand(String cmd) {
+    @Override
+    public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+            int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+            Set<String> ignoreElementList) {
+        if (isA11yTracingEnabledForTypes(loggingTypes)) {
+            mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), callingUid,
+                    callStack, timestamp, processId, threadId,
+                    ((ignoreElementList == null) ? new HashSet<String>() : ignoreElementList));
+        }
+    }
+
+    private boolean needToNotifyClients(long otherTypesEnabled) {
+        return (mEnabledLoggingFlags & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES)
+                != (otherTypesEnabled & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES);
+    }
+
+    int onShellCommand(String cmd, ShellCommand shell) {
         switch (cmd) {
             case "start-trace": {
-                startTrace();
+                String opt = shell.getNextOption();
+                if (opt == null) {
+                    startTrace(FLAGS_LOGGING_ALL);
+                    return 0;
+                }
+                List<String> types = new ArrayList<String>();
+                while (opt != null) {
+                    switch (opt) {
+                        case "-t": {
+                            String type = shell.getNextArg();
+                            while (type != null) {
+                                types.add(type);
+                                type = shell.getNextArg();
+                            }
+                            break;
+                        }
+                        default: {
+                            shell.getErrPrintWriter().println(
+                                    "Error: option not recognized " + opt);
+                            stopTrace();
+                            return -1;
+                        }
+                    }
+                    opt = shell.getNextOption();
+                }
+                long enabledTypes = AccessibilityTrace.getLoggingFlagsFromNames(types);
+                startTrace(enabledTypes);
                 return 0;
             }
             case "stop-trace": {
@@ -92,8 +217,29 @@
     }
 
     void onHelp(PrintWriter pw) {
-        pw.println("  start-trace");
-        pw.println("    Start the debug tracing.");
+        pw.println("  start-trace [-t LOGGING_TYPE [LOGGING_TYPE...]]");
+        pw.println("    Start the debug tracing. If no option is present, full trace will be");
+        pw.println("    generated. Options are:");
+        pw.println("      -t: Only generate tracing for the logging type(s) specified here.");
+        pw.println("          LOGGING_TYPE can be any one of below:");
+        pw.println("            IAccessibilityServiceConnection");
+        pw.println("            IAccessibilityServiceClient");
+        pw.println("            IAccessibilityManager");
+        pw.println("            IAccessibilityManagerClient");
+        pw.println("            IAccessibilityInteractionConnection");
+        pw.println("            IAccessibilityInteractionConnectionCallback");
+        pw.println("            IRemoteMagnificationAnimationCallback");
+        pw.println("            IWindowMagnificationConnection");
+        pw.println("            IWindowMagnificationConnectionCallback");
+        pw.println("            WindowManagerInternal");
+        pw.println("            WindowsForAccessibilityCallback");
+        pw.println("            MagnificationCallbacks");
+        pw.println("            InputFilter");
+        pw.println("            Gesture");
+        pw.println("            AccessibilityService");
+        pw.println("            PMBroadcastReceiver");
+        pw.println("            UserBroadcastReceiver");
+        pw.println("            FingerprintGesture");
         pw.println("  stop-trace");
         pw.println("    Stop the debug tracing.");
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0fde0de..c70bf73 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -392,7 +392,7 @@
         return mBoundServices;
     }
 
-    int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) {
+    int getClientStateLocked(boolean isUiAutomationRunning, int traceClientState) {
         int clientState = 0;
         final boolean a11yEnabled = isUiAutomationRunning
                 || isHandlingAccessibilityEventsLocked();
@@ -408,9 +408,9 @@
         if (mIsTextHighContrastEnabled) {
             clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
         }
-        if (isTracingEnabled) {
-            clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED;
-        }
+
+        clientState |= traceClientState;
+
         return clientState;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index ff79469..0a68d71 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
 
@@ -69,6 +71,7 @@
     private final AccessibilityEventSender mAccessibilityEventSender;
     private final AccessibilitySecurityPolicy mSecurityPolicy;
     private final AccessibilityUserManager mAccessibilityUserManager;
+    private final AccessibilityTraceManager mTraceManager;
 
     // Connections and window tokens for cross-user windows
     private final SparseArray<RemoteAccessibilityConnection>
@@ -140,26 +143,20 @@
 
         /**
          * Starts tracking windows changes from window manager by registering callback.
-         *
-         * @return true if callback registers successful.
          */
-        boolean startTrackingWindowsLocked() {
-            boolean result = true;
-
+        void startTrackingWindowsLocked() {
             if (!mTrackingWindows) {
                 // Turns on the flag before setup the callback.
                 // In some cases, onWindowsForAccessibilityChanged will be called immediately in
                 // setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
                 mTrackingWindows = true;
-                result = mWindowManagerInternal.setWindowsForAccessibilityCallback(
-                        mDisplayId, this);
-                if (!result) {
-                    mTrackingWindows = false;
-                    Slog.w(LOG_TAG, "set windowsObserver callbacks fail, displayId:"
-                            + mDisplayId);
+                if (traceWMEnabled()) {
+                    logTraceWM("setWindowsForAccessibilityCallback",
+                            "displayId=" + mDisplayId + ";callback=" + this);
                 }
+                mWindowManagerInternal.setWindowsForAccessibilityCallback(
+                        mDisplayId, this);
             }
-            return result;
         }
 
         /**
@@ -167,6 +164,10 @@
          */
         void stopTrackingWindowsLocked() {
             if (mTrackingWindows) {
+                if (traceWMEnabled()) {
+                    logTraceWM("setWindowsForAccessibilityCallback",
+                            "displayId=" + mDisplayId + ";callback=null");
+                }
                 mWindowManagerInternal.setWindowsForAccessibilityCallback(
                         mDisplayId, null);
                 mTrackingWindows = false;
@@ -474,6 +475,9 @@
             if (oldWindow.displayId != newWindow.displayId) {
                 return true;
             }
+            if (oldWindow.taskId != newWindow.taskId) {
+                return true;
+            }
             return false;
         }
 
@@ -674,6 +678,7 @@
             reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
             reportedWindow.setPictureInPicture(window.inPictureInPicture);
             reportedWindow.setDisplayId(window.displayId);
+            reportedWindow.setTaskId(window.taskId);
 
             final int parentId = findWindowIdLocked(userId, window.parentToken);
             if (parentId >= 0) {
@@ -844,19 +849,21 @@
     }
 
     /**
-     * Constructor for AccessibilityManagerService.
+     * Constructor for AccessibilityWindowManager.
      */
     public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler,
             @NonNull WindowManagerInternal windowManagerInternal,
             @NonNull AccessibilityEventSender accessibilityEventSender,
             @NonNull AccessibilitySecurityPolicy securityPolicy,
-            @NonNull AccessibilityUserManager accessibilityUserManager) {
+            @NonNull AccessibilityUserManager accessibilityUserManager,
+            @NonNull AccessibilityTraceManager traceManager) {
         mLock = lock;
         mHandler = handler;
         mWindowManagerInternal = windowManagerInternal;
         mAccessibilityEventSender = accessibilityEventSender;
         mSecurityPolicy = securityPolicy;
         mAccessibilityUserManager = accessibilityUserManager;
+        mTraceManager = traceManager;
     }
 
     /**
@@ -873,9 +880,8 @@
             if (observer.isTrackingWindowsLocked()) {
                 return;
             }
-            if (observer.startTrackingWindowsLocked()) {
-                mDisplayWindowsObservers.put(displayId, observer);
-            }
+            observer.startTrackingWindowsLocked();
+            mDisplayWindowsObservers.put(displayId, observer);
         }
     }
 
@@ -957,6 +963,9 @@
         final int windowId;
         boolean shouldComputeWindows = false;
         final IBinder token = window.asBinder();
+        if (traceWMEnabled()) {
+            logTraceWM("getDisplayIdForWindow", "token=" + token);
+        }
         final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
@@ -1003,9 +1012,15 @@
             registerIdLocked(leashToken, windowId);
         }
         if (shouldComputeWindows) {
+            if (traceWMEnabled()) {
+                logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId);
+            }
             mWindowManagerInternal.computeWindowsForAccessibility(displayId);
         }
-
+        if (traceWMEnabled()) {
+            logTraceWM("setAccessibilityIdToSurfaceMetadata",
+                    "token=" + token + ";windowId=" + windowId);
+        }
         mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
         return windowId;
     }
@@ -1139,6 +1154,10 @@
             mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
         }
         if (binder != null) {
+            if (traceWMEnabled()) {
+                logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder
+                        + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID");
+            }
             mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
                     binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
         }
@@ -1169,6 +1188,9 @@
      * @return The userId
      */
     public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
+        if (traceWMEnabled()) {
+            logTraceWM("getWindowOwnerUserId", "token=" + windowToken);
+        }
         return mWindowManagerInternal.getWindowOwnerUserId(windowToken);
     }
 
@@ -1547,6 +1569,10 @@
         for (int i = 0; i < connectionList.size(); i++) {
             final RemoteAccessibilityConnection connection = connectionList.get(i);
             if (connection != null) {
+                if (traceIntConnEnabled()) {
+                    logTraceIntConn("notifyOutsideTouch");
+                }
+
                 try {
                     connection.getRemote().notifyOutsideTouch();
                 } catch (RemoteException re) {
@@ -1567,6 +1593,9 @@
      */
     public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
         final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+        if (traceWMEnabled()) {
+            logTraceWM("getDisplayIdForWindow", "token=" + windowToken);
+        }
         final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
         return displayId;
     }
@@ -1595,6 +1624,9 @@
      * @return The input focused windowId, or -1 if not found
      */
     private int findFocusedWindowId(int userId) {
+        if (traceWMEnabled()) {
+            logTraceWM("getFocusedWindowToken", "");
+        }
         final IBinder token = mWindowManagerInternal.getFocusedWindowToken();
         synchronized (mLock) {
             return findWindowIdLocked(userId, token);
@@ -1644,6 +1676,9 @@
                 return;
             }
         }
+        if (traceIntConnEnabled()) {
+            logTraceIntConn("notifyOutsideTouch");
+        }
         try {
             connection.getRemote().clearAccessibilityFocus();
         } catch (RemoteException re) {
@@ -1666,6 +1701,25 @@
         return null;
     }
 
+    private boolean traceWMEnabled() {
+        return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+    }
+
+    private void logTraceWM(String methodName, String params) {
+        mTraceManager.logTrace("WindowManagerInternal." + methodName,
+                    FLAGS_WINDOW_MANAGER_INTERNAL, params);
+    }
+
+    private boolean traceIntConnEnabled() {
+        return mTraceManager.isA11yTracingEnabledForTypes(
+                FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+    }
+
+    private void logTraceIntConn(String methodName) {
+        mTraceManager.logTrace(
+                    LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+    }
+
     /**
      * Associate the token of the embedded view hierarchy to the host view hierarchy.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index f5b0eb1..95f3560 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility;
 
+import android.accessibilityservice.AccessibilityTrace;
 import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -56,6 +57,7 @@
 
     private static final String LOG_TAG = AutoclickController.class.getSimpleName();
 
+    private final AccessibilityTraceManager mTrace;
     private final Context mContext;
     private final int mUserId;
 
@@ -63,13 +65,18 @@
     private ClickScheduler mClickScheduler;
     private ClickDelayObserver mClickDelayObserver;
 
-    public AutoclickController(Context context, int userId) {
+    public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
+        mTrace = trace;
         mContext = context;
         mUserId = userId;
     }
 
     @Override
     public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+            mTrace.logTrace(LOG_TAG + ".onMotionEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             if (mClickScheduler == null) {
                 Handler handler = new Handler(mContext.getMainLooper());
@@ -89,6 +96,10 @@
 
     @Override
     public void onKeyEvent(KeyEvent event, int policyFlags) {
+        if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+            mTrace.logTrace(LOG_TAG + ".onKeyEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+                    "event=" + event + ";policyFlags=" + policyFlags);
+        }
         if (mClickScheduler != null) {
             if (KeyEvent.isModifierKey(event.getKeyCode())) {
                 mClickScheduler.updateMetaState(event.getMetaState());
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
index bc379c2..b8250c0 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
@@ -64,6 +66,10 @@
 
     @Override
     public void onKeyEvent(KeyEvent event, int policyFlags) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(FLAGS_INPUT_FILTER)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
+                    FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
+        }
         /*
          * Certain keys have system-level behavior that affects accessibility services.
          * Let that behavior settle before handling the keys
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 2673cd1..5cbd1a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility;
 
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.GestureStep;
 import android.accessibilityservice.GestureDescription.TouchPoint;
@@ -68,6 +69,7 @@
     private final Handler mHandler;
     private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
 
+    private final AccessibilityTraceManager mTrace;
     private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
     private IntArray mSequencesInProgress = new IntArray(5);
     private boolean mIsDestroyed = false;
@@ -80,15 +82,17 @@
     /**
      * @param looper A looper on the main thread to use for dispatching new events
      */
-    public MotionEventInjector(Looper looper) {
+    public MotionEventInjector(Looper looper, AccessibilityTraceManager trace) {
         mHandler = new Handler(looper, this);
+        mTrace = trace;
     }
 
     /**
      * @param handler A handler to post messages. Exposes internal state for testing only.
      */
-    public MotionEventInjector(Handler handler) {
+    public MotionEventInjector(Handler handler, AccessibilityTraceManager trace) {
         mHandler = handler;
+        mTrace = trace;
     }
 
     /**
@@ -112,6 +116,12 @@
 
     @Override
     public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mTrace.isA11yTracingEnabledForTypes(
+                AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+            mTrace.logTrace(LOG_TAG + ".onMotionEvent",
+                    AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         // MotionEventInjector would cancel any injected gesture when any MotionEvent arrives.
         // For user using an external device to control the pointer movement, it's almost
         // impossible to perform the gestures. Any slightly unintended movement results in the
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 9547280..7ee0690 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.Nullable;
 import android.app.UiAutomation;
@@ -126,7 +127,6 @@
             mUiAutomationServiceOwner = owner;
             mUiAutomationServiceInfo = accessibilityServiceInfo;
             mUiAutomationService.mServiceInterface = serviceClient;
-            mUiAutomationService.onAdded();
             try {
                 mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
                         0);
@@ -136,6 +136,8 @@
                 return;
             }
 
+            mUiAutomationService.onAdded();
+
             mUiAutomationService.connectServiceUnknownThread();
         }
     }
@@ -269,6 +271,14 @@
                     // If the serviceInterface is null, the UiAutomation has been shut down on
                     // another thread.
                     if (serviceInterface != null) {
+                        if (mTrace.isA11yTracingEnabledForTypes(
+                                AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+                            mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
+                                    AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
+                                    "serviceConnection=" + this + ";connectionId=" + mId
+                                    + "windowToken="
+                                    + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+                        }
                         serviceInterface.init(this, mId,
                                 mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
                     }
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 d7bc040..74f0bcb 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility.gestures;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -82,6 +84,7 @@
         implements GestureManifold.Listener {
 
     static final boolean DEBUG = false;
+    private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
 
     // Tag for logging received events.
     private static final String LOG_TAG = "TouchExplorer";
@@ -254,6 +257,10 @@
 
     @Override
     public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
             super.onMotionEvent(event, rawEvent, policyFlags);
             return;
@@ -308,6 +315,10 @@
 
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent",
+                    LOGGING_FLAGS, "event=" + event);
+        }
         final int eventType = event.getEventType();
 
         if (eventType == TYPE_VIEW_HOVER_EXIT) {
@@ -346,6 +357,10 @@
 
     @Override
     public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
             if (isSendMotionEventsEnabled()) {
@@ -362,6 +377,10 @@
 
     @Override
     public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         mAms.onTouchInteractionEnd();
         // Remove pending event deliveries.
         mSendHoverEnterAndMoveDelayed.cancel();
@@ -394,6 +413,9 @@
 
     @Override
     public boolean onGestureStarted() {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS);
+        }
         // We have to perform gesture detection, so
         // clear the current state and try to detect.
         mSendHoverEnterAndMoveDelayed.cancel();
@@ -407,6 +429,10 @@
 
     @Override
     public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted",
+                    LOGGING_FLAGS, "event=" + gestureEvent);
+        }
         endGestureDetection(true);
         mSendTouchInteractionEndDelayed.cancel();
         dispatchGesture(gestureEvent);
@@ -415,6 +441,10 @@
 
     @Override
     public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+            mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         if (mState.isGestureDetecting()) {
             endGestureDetection(event.getActionMasked() == ACTION_UP);
             return true;
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 1f66bfd..718da9e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
@@ -46,6 +48,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.Locale;
@@ -135,6 +138,10 @@
          */
         @GuardedBy("mLock")
         boolean register() {
+            if (traceEnabled()) {
+                logTrace("setMagnificationCallbacks",
+                        "displayID=" + mDisplayId + ";callback=" + this);
+            }
             mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
                     mDisplayId, this);
             if (!mRegistered) {
@@ -142,6 +149,10 @@
                 return false;
             }
             mSpecAnimationBridge.setEnabled(true);
+            if (traceEnabled()) {
+                logTrace("getMagnificationRegion",
+                        "displayID=" + mDisplayId + ";region=" + mMagnificationRegion);
+            }
             // Obtain initial state.
             mControllerCtx.getWindowManager().getMagnificationRegion(
                     mDisplayId, mMagnificationRegion);
@@ -162,6 +173,10 @@
         void unregister(boolean delete) {
             if (mRegistered) {
                 mSpecAnimationBridge.setEnabled(false);
+                if (traceEnabled()) {
+                    logTrace("setMagnificationCallbacks",
+                            "displayID=" + mDisplayId + ";callback=null");
+                }
                 mControllerCtx.getWindowManager().setMagnificationCallbacks(
                         mDisplayId, null);
                 mMagnificationRegion.setEmpty();
@@ -268,7 +283,7 @@
         }
 
         @Override
-        public void onRotationChanged(int rotation) {
+        public void onDisplaySizeChanged() {
             // Treat as context change and reset
             final Message m = PooledLambda.obtainMessage(
                     FullScreenMagnificationController::resetIfNeeded,
@@ -431,6 +446,10 @@
         void setForceShowMagnifiableBounds(boolean show) {
             if (mRegistered) {
                 mForceShowMagnifiableBounds = show;
+                if (traceEnabled()) {
+                    logTrace("setForceShowMagnifiableBounds",
+                            "displayID=" + mDisplayId + ";show=" + show);
+                }
                 mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
                         mDisplayId, show);
             }
@@ -1255,6 +1274,16 @@
         }
     }
 
+    private boolean traceEnabled() {
+        return mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+                FLAGS_WINDOW_MANAGER_INTERNAL);
+    }
+
+    private void logTrace(String methodName, String params) {
+        mControllerCtx.getTraceManager().logTrace(
+                "WindowManagerInternal." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+    }
+
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
@@ -1320,6 +1349,13 @@
                     mEnabled = enabled;
                     if (!mEnabled) {
                         mSentMagnificationSpec.clear();
+                        if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+                                FLAGS_WINDOW_MANAGER_INTERNAL)) {
+                            mControllerCtx.getTraceManager().logTrace(
+                                    "WindowManagerInternal.setMagnificationSpec",
+                                    FLAGS_WINDOW_MANAGER_INTERNAL,
+                                    "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+                        }
                         mControllerCtx.getWindowManager().setMagnificationSpec(
                                 mDisplayId, mSentMagnificationSpec);
                     }
@@ -1367,6 +1403,13 @@
                 }
 
                 mSentMagnificationSpec.setTo(spec);
+                if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+                        FLAGS_WINDOW_MANAGER_INTERNAL)) {
+                    mControllerCtx.getTraceManager().logTrace(
+                            "WindowManagerInternal.setMagnificationSpec",
+                            FLAGS_WINDOW_MANAGER_INTERNAL,
+                            "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+                }
                 mControllerCtx.getWindowManager().setMagnificationSpec(
                         mDisplayId, mSentMagnificationSpec);
             }
@@ -1455,6 +1498,7 @@
     public static class ControllerContext {
         private final Context mContext;
         private final AccessibilityManagerService mAms;
+        private final AccessibilityTraceManager mTrace;
         private final WindowManagerInternal mWindowManager;
         private final Handler mHandler;
         private final Long mAnimationDuration;
@@ -1469,6 +1513,7 @@
                 long animationDuration) {
             mContext = context;
             mAms = ams;
+            mTrace = ams.getTraceManager();
             mWindowManager = windowManager;
             mHandler = handler;
             mAnimationDuration = animationDuration;
@@ -1491,6 +1536,14 @@
         }
 
         /**
+         * @return AccessibilityTraceManager
+         */
+        @NonNull
+        public AccessibilityTraceManager getTraceManager() {
+            return mTrace;
+        }
+
+        /**
          * @return WindowManagerInternal
          */
         @NonNull
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f7d1b9a..c3d8d4c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -61,6 +61,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.gestures.GestureUtils;
 
 /**
@@ -142,12 +143,13 @@
 
     public FullScreenMagnificationGestureHandler(@UiContext Context context,
             FullScreenMagnificationController fullScreenMagnificationController,
+            AccessibilityTraceManager trace,
             Callback callback,
             boolean detectTripleTap,
             boolean detectShortcutTrigger,
             @NonNull WindowMagnificationPromptController promptController,
             int displayId) {
-        super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+        super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
         if (DEBUG_ALL) {
             Log.i(mLogTag,
                     "FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
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 f9aecd7..8aacafb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -411,8 +411,7 @@
         synchronized (mLock) {
             if (mWindowMagnificationMgr == null) {
                 mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
-                        mAms.getCurrentUserIdLocked(),
-                        this);
+                        mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
             }
             return mWindowMagnificationMgr;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index bbe40b6..19b3396 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -20,11 +20,13 @@
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_UP;
 
+import android.accessibilityservice.AccessibilityTrace;
 import android.annotation.NonNull;
 import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.BaseEventStreamTransformation;
 
 import java.util.ArrayDeque;
@@ -99,14 +101,17 @@
         void onTripleTapped(int displayId, int mode);
     }
 
+    private final AccessibilityTraceManager mTrace;
     protected final Callback mCallback;
 
     protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
             boolean detectShortcutTrigger,
+            AccessibilityTraceManager trace,
             @NonNull Callback callback) {
         mDisplayId = displayId;
         mDetectTripleTap = detectTripleTap;
         mDetectShortcutTrigger = detectShortcutTrigger;
+        mTrace = trace;
         mCallback = callback;
 
         mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
@@ -118,6 +123,12 @@
         if (DEBUG_ALL) {
             Slog.i(mLogTag, "onMotionEvent(" + event + ")");
         }
+        if (mTrace.isA11yTracingEnabledForTypes(
+                AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+            mTrace.logTrace("MagnificationGestureHandler.onMotionEvent",
+                    AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+                    "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+        }
         if (DEBUG_EVENT_STREAM) {
             storeEventInto(mDebugInputEventHistory, event);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 993027d..5277425 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -16,6 +16,9 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
 import static android.os.IBinder.DeathRecipient;
 
 import android.annotation.NonNull;
@@ -27,6 +30,8 @@
 import android.view.accessibility.IWindowMagnificationConnectionCallback;
 import android.view.accessibility.MagnificationAnimationCallback;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
+
 /**
  * A wrapper of {@link IWindowMagnificationConnection}.
  */
@@ -36,9 +41,12 @@
     private static final String TAG = "WindowMagnificationConnectionWrapper";
 
     private final @NonNull IWindowMagnificationConnection mConnection;
+    private final @NonNull AccessibilityTraceManager mTrace;
 
-    WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+    WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+            @NonNull AccessibilityTraceManager trace) {
         mConnection = connection;
+        mTrace = trace;
     }
 
     //Should not use this instance anymore after calling it.
@@ -52,9 +60,15 @@
 
     boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
             @Nullable MagnificationAnimationCallback callback) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".enableWindowMagnification",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+                    "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+                    + ";centerY=" + centerY + ";callback=" + callback);
+        }
         try {
             mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
-                    transformToRemoteCallback(callback));
+                    transformToRemoteCallback(callback, mTrace));
         } catch (RemoteException e) {
             if (DBG) {
                 Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -65,6 +79,10 @@
     }
 
     boolean setScale(int displayId, float scale) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+                    "displayId=" + displayId + ";scale=" + scale);
+        }
         try {
             mConnection.setScale(displayId, scale);
         } catch (RemoteException e) {
@@ -78,8 +96,14 @@
 
     boolean disableWindowMagnification(int displayId,
             @Nullable MagnificationAnimationCallback callback) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".disableWindowMagnification",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+                    "displayId=" + displayId + ";callback=" + callback);
+        }
         try {
-            mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
+            mConnection.disableWindowMagnification(displayId,
+                    transformToRemoteCallback(callback, mTrace));
         } catch (RemoteException e) {
             if (DBG) {
                 Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -90,6 +114,10 @@
     }
 
     boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+                    "displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY);
+        }
         try {
             mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
         } catch (RemoteException e) {
@@ -102,6 +130,11 @@
     }
 
     boolean showMagnificationButton(int displayId, int magnificationMode) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".showMagnificationButton",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+                    "displayId=" + displayId + ";mode=" + magnificationMode);
+        }
         try {
             mConnection.showMagnificationButton(displayId, magnificationMode);
         } catch (RemoteException e) {
@@ -114,6 +147,10 @@
     }
 
     boolean removeMagnificationButton(int displayId) {
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".removeMagnificationButton",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+        }
         try {
             mConnection.removeMagnificationButton(displayId);
         } catch (RemoteException e) {
@@ -126,6 +163,14 @@
     }
 
     boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+        if (mTrace.isA11yTracingEnabledForTypes(
+                FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+                | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+            mTrace.logTrace(TAG + ".setConnectionCallback",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+                    | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                    "callback=" + connectionCallback);
+        }
         try {
             mConnection.setConnectionCallback(connectionCallback);
         } catch (RemoteException e) {
@@ -139,25 +184,38 @@
 
     private static @Nullable
             IRemoteMagnificationAnimationCallback transformToRemoteCallback(
-            MagnificationAnimationCallback callback) {
+            MagnificationAnimationCallback callback, AccessibilityTraceManager trace) {
         if (callback == null) {
             return null;
         }
-        return new RemoteAnimationCallback(callback);
+        return new RemoteAnimationCallback(callback, trace);
     }
 
     private static class RemoteAnimationCallback extends
             IRemoteMagnificationAnimationCallback.Stub {
-
         private final MagnificationAnimationCallback mCallback;
+        private final AccessibilityTraceManager mTrace;
 
-        RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+        RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback,
+                               @NonNull AccessibilityTraceManager trace) {
             mCallback = callback;
+            mTrace = trace;
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+                mTrace.logTrace("RemoteAnimationCallback.constructor",
+                        FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "callback=" + callback);
+            }
         }
 
         @Override
         public void onResult(boolean success) throws RemoteException {
             mCallback.onResult(success);
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+                mTrace.logTrace("RemoteAnimationCallback.onResult",
+                        FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "success=" + success);
+            }
+
         }
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 4fb9a03..b26d364 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -34,6 +34,7 @@
 import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.gestures.MultiTap;
 import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -89,9 +90,10 @@
 
     public WindowMagnificationGestureHandler(@UiContext Context context,
             WindowMagnificationManager windowMagnificationMgr,
+            AccessibilityTraceManager trace,
             Callback callback,
             boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
-        super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+        super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
         if (DEBUG_ALL) {
             Slog.i(mLogTag,
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 938cb73..7a111d8 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -16,6 +16,9 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -39,6 +42,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 /**
@@ -111,11 +115,14 @@
     }
 
     private final Callback mCallback;
+    private final AccessibilityTraceManager mTrace;
 
-    public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback) {
+    public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+            AccessibilityTraceManager trace) {
         mContext = context;
         mUserId = userId;
         mCallback = callback;
+        mTrace = trace;
     }
 
     /**
@@ -135,7 +142,7 @@
                 mConnectionWrapper = null;
             }
             if (connection != null) {
-                mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+                mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
             }
 
             if (mConnectionWrapper != null) {
@@ -197,7 +204,10 @@
                 }
             }
         }
-
+        if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+            mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
             final StatusBarManagerInternal service = LocalServices.getService(
@@ -511,6 +521,12 @@
 
         @Override
         public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId + ";bounds=" + bounds);
+            }
             synchronized (mLock) {
                 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
                 if (magnifier == null) {
@@ -527,11 +543,23 @@
         @Override
         public void onChangeMagnificationMode(int displayId, int magnificationMode)
                 throws RemoteException {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId + ";mode=" + magnificationMode);
+            }
             //TODO: Uses this method to change the magnification mode on non-default display.
         }
 
         @Override
         public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId + ";source=" + sourceBounds);
+            }
             synchronized (mLock) {
                 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
                 if (magnifier == null) {
@@ -543,11 +571,23 @@
 
         @Override
         public void onPerformScaleAction(int displayId, float scale) {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId + ";scale=" + scale);
+            }
             mCallback.onPerformScaleAction(displayId, scale);
         }
 
         @Override
         public void onAccessibilityActionPerformed(int displayId) {
+            if (mTrace.isA11yTracingEnabledForTypes(
+                    FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+                mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed",
+                        FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+                        "displayId=" + displayId);
+            }
             mCallback.onAccessibilityActionPerformed(displayId);
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index de5f47d..012c5b1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -618,7 +618,7 @@
                 + " for " + durationMs + "ms");
         enforceCallingPermissionForManagement();
 
-        Preconditions.checkNotNull(serviceName);
+        Objects.requireNonNull(serviceName);
         if (durationMs > MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS) {
             throw new IllegalArgumentException("Max duration is "
                     + MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS + " (called with " + durationMs + ")");
@@ -929,7 +929,7 @@
 
         void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
                 long expiration) {
-            Preconditions.checkNotNull(packageName);
+            Objects.requireNonNull(packageName);
             synchronized (mLock) {
                 AutofillDisabledInfo info =
                         getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -939,7 +939,7 @@
 
         void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
                 long expiration) {
-            Preconditions.checkNotNull(componentName);
+            Objects.requireNonNull(componentName);
             synchronized (mLock) {
                 AutofillDisabledInfo info =
                         getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -949,7 +949,7 @@
 
         boolean isAutofillDisabledLocked(@UserIdInt int userId,
                 @NonNull ComponentName componentName) {
-            Preconditions.checkNotNull(componentName);
+            Objects.requireNonNull(componentName);
             final boolean disabled;
             synchronized (mLock) {
                 final AutofillDisabledInfo info = mCache.get(userId);
@@ -959,7 +959,7 @@
         }
 
         long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
-            Preconditions.checkNotNull(packageName);
+            Objects.requireNonNull(packageName);
             final Long expiration;
             synchronized (mLock) {
                 final AutofillDisabledInfo info = mCache.get(userId);
@@ -971,7 +971,7 @@
         @Nullable
         ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
                 @NonNull String packageName) {
-            Preconditions.checkNotNull(packageName);
+            Objects.requireNonNull(packageName);
             final ArrayMap<String, Long> disabledList;
             synchronized (mLock) {
                 final AutofillDisabledInfo info = mCache.get(userId);
@@ -1593,8 +1593,8 @@
                 @NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
                 throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
-            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
-            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+            activityToken = Objects.requireNonNull(activityToken, "activityToken");
+            appCallback = Objects.requireNonNull(appCallback, "appCallback");
 
             boolean restored = false;
             synchronized (mLock) {
@@ -1693,7 +1693,7 @@
 
         @Override
         public void onPendingSaveUi(int operation, IBinder token) {
-            Preconditions.checkNotNull(token, "token");
+            Objects.requireNonNull(token, "token");
             Preconditions.checkArgument(operation == AutofillManager.PENDING_UI_OPERATION_CANCEL
                     || operation == AutofillManager.PENDING_UI_OPERATION_RESTORE,
                     "invalid operation: %d", operation);
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
new file mode 100644
index 0000000..715697d
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofill;
+
+import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Maintains a client suggestions session with the
+ * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
+ *
+ */
+final class ClientSuggestionsSession {
+
+    private static final String TAG = "ClientSuggestionsSession";
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
+
+    private final int mSessionId;
+    private final IAutoFillManagerClient mClient;
+    private final Handler mHandler;
+    private final ComponentName mComponentName;
+
+    private final RemoteFillService.FillServiceCallbacks mCallbacks;
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private AndroidFuture<FillResponse> mPendingFillRequest;
+    @GuardedBy("mLock")
+    private int mPendingFillRequestId = INVALID_REQUEST_ID;
+
+    ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
+            ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
+        mSessionId = sessionId;
+        mClient = client;
+        mHandler = handler;
+        mComponentName = componentName;
+        mCallbacks = callbacks;
+    }
+
+    void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
+        final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
+        final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
+
+        mHandler.post(() -> {
+            if (sVerbose) {
+                Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
+            }
+
+            try {
+                mClient.requestFillFromClient(requestId, inlineRequest,
+                        new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
+            } catch (RemoteException e) {
+                fillRequest.completeExceptionally(e);
+            }
+        });
+
+        fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+        futureRef.set(fillRequest);
+
+        synchronized (mLock) {
+            mPendingFillRequest = fillRequest;
+            mPendingFillRequestId = requestId;
+        }
+
+        fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
+            synchronized (mLock) {
+                mPendingFillRequest = null;
+                mPendingFillRequestId = INVALID_REQUEST_ID;
+            }
+            if (err == null) {
+                processAutofillId(res);
+                mCallbacks.onFillRequestSuccess(requestId, res,
+                        mComponentName.getPackageName(), flags);
+            } else {
+                Slog.e(TAG, "Error calling on  client fill request", err);
+                if (err instanceof TimeoutException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                    mCallbacks.onFillRequestTimeout(requestId);
+                } else if (err instanceof CancellationException) {
+                    dispatchCancellationSignal(cancellationSink.get());
+                } else {
+                    mCallbacks.onFillRequestFailure(requestId, err.getMessage());
+                }
+            }
+        }));
+    }
+
+    /**
+     * Gets the application info for the component.
+     */
+    @Nullable
+    static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
+        try {
+            ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
+                    comp.getPackageName(),
+                    PackageManager.GET_META_DATA,
+                    userId);
+            if (si != null) {
+                return si;
+            }
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Gets the user-visible name of the application.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
+        return appInfo == null ? null : appInfo.loadSafeLabel(
+                context.getPackageManager(), 0 /* do not ellipsize */,
+                TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+    }
+
+    /**
+     * Gets the user-visible icon of the application.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
+        return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
+    }
+
+    int cancelCurrentRequest() {
+        synchronized (mLock) {
+            return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+                    ? mPendingFillRequestId
+                    : INVALID_REQUEST_ID;
+        }
+    }
+
+    /**
+     * The {@link AutofillId} which the client gets from its view is not contain the session id,
+     * but Autofill framework is using the {@link AutofillId} with a session id. So before using
+     * those ids in the Autofill framework, applies the current session id.
+     *
+     * @param res which response need to apply for a session id
+     */
+    private void processAutofillId(FillResponse res) {
+        if (res == null) {
+            return;
+        }
+
+        final List<Dataset> datasets = res.getDatasets();
+        if (datasets != null && !datasets.isEmpty()) {
+            for (int i = 0; i < datasets.size(); i++) {
+                final Dataset dataset = datasets.get(i);
+                if (dataset != null) {
+                    applySessionId(dataset.getFieldIds());
+                }
+            }
+        }
+
+        final SaveInfo saveInfo = res.getSaveInfo();
+        if (saveInfo != null) {
+            applySessionId(saveInfo.getOptionalIds());
+            applySessionId(saveInfo.getRequiredIds());
+            applySessionId(saveInfo.getSanitizerValues());
+            applySessionId(saveInfo.getTriggerId());
+        }
+    }
+
+    private void applySessionId(List<AutofillId> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return;
+        }
+
+        for (int i = 0; i < ids.size(); i++) {
+            applySessionId(ids.get(i));
+        }
+    }
+
+    private void applySessionId(AutofillId[][] ids) {
+        if (ids == null) {
+            return;
+        }
+        for (int i = 0; i < ids.length; i++) {
+            applySessionId(ids[i]);
+        }
+    }
+
+    private void applySessionId(AutofillId[] ids) {
+        if (ids == null) {
+            return;
+        }
+        for (int i = 0; i < ids.length; i++) {
+            applySessionId(ids[i]);
+        }
+    }
+
+    private void applySessionId(AutofillId id) {
+        if (id == null) {
+            return;
+        }
+        id.setSessionId(mSessionId);
+    }
+
+    private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+        if (signal == null) {
+            return;
+        }
+        try {
+            signal.cancel();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error requesting a cancellation", e);
+        }
+    }
+
+    private class FillCallbackImpl extends IFillCallback.Stub {
+        final AndroidFuture<FillResponse> mFillRequest;
+        final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
+        final AtomicReference<ICancellationSignal> mCancellationSink;
+
+        FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
+                AtomicReference<AndroidFuture<FillResponse>> futureRef,
+                AtomicReference<ICancellationSignal> cancellationSink) {
+            mFillRequest = fillRequest;
+            mFutureRef = futureRef;
+            mCancellationSink = cancellationSink;
+        }
+
+        @Override
+        public void onCancellable(ICancellationSignal cancellation) {
+            AndroidFuture<FillResponse> future = mFutureRef.get();
+            if (future != null && future.isCancelled()) {
+                dispatchCancellationSignal(cancellation);
+            } else {
+                mCancellationSink.set(cancellation);
+            }
+        }
+
+        @Override
+        public void onSuccess(FillResponse response) {
+            mFillRequest.complete(response);
+        }
+
+        @Override
+        public void onFailure(int requestId, CharSequence message) {
+            String errorMessage = message == null ? "" : String.valueOf(message);
+            mFillRequest.completeExceptionally(
+                    new RuntimeException(errorMessage));
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 078d908..11df871 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -53,6 +54,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -346,6 +348,9 @@
      */
     private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
 
+    @Nullable
+    private ClientSuggestionsSession mClientSuggestionsSession;
+
     void onSwitchInputMethodLocked() {
         // One caveat is that for the case where the focus is on a field for which regular autofill
         // returns null, and augmented autofill is triggered,  and then the user switches the input
@@ -416,6 +421,10 @@
         /** Whether the current {@link FillResponse} is expired. */
         @GuardedBy("mLock")
         private boolean mExpiredResponse;
+
+        /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
+        @GuardedBy("mLock")
+        private boolean mClientSuggestionsEnabled;
     }
 
     /**
@@ -441,13 +450,19 @@
                         return;
                     }
                     mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
-                    maybeRequestFillLocked();
+                    maybeRequestFillFromServiceLocked();
                     viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
                 }
             } : null;
         }
 
-        void maybeRequestFillLocked() {
+        void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
+            mPendingFillRequest = null;
+            mWaitForInlineRequest = inlineRequest != null;
+            mPendingInlineSuggestionsRequest = inlineRequest;
+        }
+
+        void maybeRequestFillFromServiceLocked() {
             if (mPendingFillRequest == null) {
                 return;
             }
@@ -457,9 +472,12 @@
                     return;
                 }
 
-                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
-                        mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
-                        mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
+                    mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+                            mPendingFillRequest.getFillContexts(),
+                            mPendingFillRequest.getClientState(),
+                            mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                }
             }
 
             mRemoteFillService.onFillRequest(mPendingFillRequest);
@@ -566,7 +584,7 @@
                         /*inlineSuggestionsRequest=*/null);
 
                 mPendingFillRequest = request;
-                maybeRequestFillLocked();
+                maybeRequestFillFromServiceLocked();
             }
 
             if (mActivityToken != null) {
@@ -728,30 +746,39 @@
     }
 
     /**
-     * Cancels the last request sent to the {@link #mRemoteFillService}.
+     * Cancels the last request sent to the {@link #mRemoteFillService} or the
+     * {@link #mClientSuggestionsSession}.
      */
     @GuardedBy("mLock")
     private void cancelCurrentRequestLocked() {
-        if (mRemoteFillService == null) {
-            wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
-                    + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+        if (mRemoteFillService == null && mClientSuggestionsSession == null) {
+            wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
+                    + "client suggestions session.  mForAugmentedAutofillOnly: %s",
+                    mSessionFlags.mAugmentedAutofillOnly);
             return;
         }
-        final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
 
-        // Remove the FillContext as there will never be a response for the service
-        if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
-            final int numContexts = mContexts.size();
+        if (mRemoteFillService != null) {
+            final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
 
-            // It is most likely the last context, hence search backwards
-            for (int i = numContexts - 1; i >= 0; i--) {
-                if (mContexts.get(i).getRequestId() == canceledRequest) {
-                    if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
-                    mContexts.remove(i);
-                    break;
+            // Remove the FillContext as there will never be a response for the service
+            if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+                final int numContexts = mContexts.size();
+
+                // It is most likely the last context, hence search backwards
+                for (int i = numContexts - 1; i >= 0; i--) {
+                    if (mContexts.get(i).getRequestId() == canceledRequest) {
+                        if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+                        mContexts.remove(i);
+                        break;
+                    }
                 }
             }
         }
+
+        if (mClientSuggestionsSession != null) {
+            mClientSuggestionsSession.cancelCurrentRequest();
+        }
     }
 
     private boolean isViewFocusedLocked(int flags) {
@@ -816,17 +843,30 @@
         // 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
-        // the render service is available except the autofill is triggered manually and the view
-        // is also not focused.
+        // Only ask IME to create inline suggestions request when
+        // 1. Autofill provider supports it or client enabled client suggestions.
+        // 2. The render service is available.
+        // 3. The view is focused. (The view may not be focused if the autofill is triggered
+        //    manually.)
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
-        if (mSessionFlags.mInlineSupportedByService
+        if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
                 && remoteRenderService != null
                 && isViewFocusedLocked(flags)) {
-            Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
-                    mAssistReceiver.newAutofillRequestLocked(viewState,
-                            /* isInlineRequest= */ true);
+            Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
+            if (mSessionFlags.mClientSuggestionsEnabled) {
+                final int finalRequestId = requestId;
+                inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
+                    // Using client suggestions
+                    synchronized (mLock) {
+                        onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
+                    }
+                    viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+                };
+            } else {
+                inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
+                        viewState, /* isInlineRequest= */ true);
+            }
             if (inlineSuggestionsRequestConsumer != null) {
                 final AutofillId focusedId = mCurrentViewId;
                 final int requestIdCopy = requestId;
@@ -842,11 +882,24 @@
                 );
                 viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
             }
+        } else if (mSessionFlags.mClientSuggestionsEnabled) {
+            // Request client suggestions for the dropdown mode
+            onClientFillRequestLocked(requestId, null);
         } else {
             mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
         }
 
+        if (mSessionFlags.mClientSuggestionsEnabled) {
+            // Using client suggestions, unnecessary request AssistStructure
+            return;
+        }
+
         // Now request the assist structure data.
+        requestAssistStructureLocked(requestId, flags);
+    }
+
+    @GuardedBy("mLock")
+    private void requestAssistStructureLocked(int requestId, int flags) {
         try {
             final Bundle receiverExtras = new Bundle();
             receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
@@ -895,10 +948,13 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
         mSessionState = STATE_ACTIVE;
+
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
             mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
             mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+            mSessionFlags.mClientSuggestionsEnabled =
+                    (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
             setClientLocked(client);
         }
 
@@ -1010,12 +1066,13 @@
                 if (requestLog != null) {
                     requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
                 }
-                processNullResponseLocked(requestId, requestFlags);
+                processNullResponseOrFallbackLocked(requestId, requestFlags);
                 return;
             }
 
             fieldClassificationIds = response.getFieldClassificationIds();
-            if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
+            if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
+                    && !mService.isFieldClassificationEnabledLocked()) {
                 Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
                 processNullResponseLocked(requestId, requestFlags);
                 return;
@@ -1094,6 +1151,26 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void processNullResponseOrFallbackLocked(int requestId, int flags) {
+        if (!mSessionFlags.mClientSuggestionsEnabled) {
+            processNullResponseLocked(requestId, flags);
+            return;
+        }
+
+        // fallback to the default platform password manager
+        mSessionFlags.mClientSuggestionsEnabled = false;
+
+        final InlineSuggestionsRequest inlineRequest =
+                (mLastInlineSuggestionsRequest != null
+                        && mLastInlineSuggestionsRequest.first == requestId)
+                        ? mLastInlineSuggestionsRequest.second : null;
+        mAssistReceiver.newAutofillRequestLocked(inlineRequest);
+        requestAssistStructureLocked(requestId,
+                flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
+        return;
+    }
+
     // FillServiceCallbacks
     @Override
     public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3042,13 +3119,22 @@
             filterText = value.getTextValue().toString();
         }
 
-        final CharSequence serviceLabel;
-        final Drawable serviceIcon;
+        final CharSequence targetLabel;
+        final Drawable targetIcon;
         synchronized (mLock) {
-            serviceLabel = mService.getServiceLabelLocked();
-            serviceIcon = mService.getServiceIconLocked();
+            if (mSessionFlags.mClientSuggestionsEnabled) {
+                final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
+                        mService.getUserId());
+                targetLabel = ClientSuggestionsSession.getAppLabelLocked(
+                        mService.getMaster().getContext(), appInfo);
+                targetIcon = ClientSuggestionsSession.getAppIconLocked(
+                        mService.getMaster().getContext(), appInfo);
+            } else {
+                targetLabel = mService.getServiceLabelLocked();
+                targetIcon = mService.getServiceIconLocked();
+            }
         }
-        if (serviceLabel == null || serviceIcon == null) {
+        if (targetLabel == null || targetIcon == null) {
             wtf(null, "onFillReady(): no service label or icon");
             return;
         }
@@ -3068,7 +3154,7 @@
 
         getUiForShowing().showFillUi(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName,
-                serviceLabel, serviceIcon, this, id, mCompatMode);
+                targetLabel, targetIcon, this, id, mCompatMode);
 
         mService.logDatasetShown(id, mClientState);
 
@@ -3115,6 +3201,17 @@
             return false;
         }
 
+        final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
+        if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
+                || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
+            if (sDebug) {
+                Slog.d(TAG, "Inline suggestions not supported for "
+                        + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
+                        + ". Falling back to dropdown.");
+            }
+            return false;
+        }
+
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if (remoteRenderService == null) {
@@ -3123,7 +3220,7 @@
         }
 
         final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
-                new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+                new InlineFillUi.InlineFillUiInfo(request, focusedId,
                         filterText, remoteRenderService, userId, id);
         InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
                 new InlineFillUi.InlineSuggestionUiCallback() {
@@ -3725,6 +3822,25 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void onClientFillRequestLocked(int requestId,
+            InlineSuggestionsRequest inlineSuggestionsRequest) {
+        if (mClientSuggestionsSession == null) {
+            mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
+                    mComponentName, this);
+        }
+
+        if (mContexts == null) {
+            mContexts = new ArrayList<>(1);
+        }
+
+        if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
+            inlineSuggestionsRequest = null;
+        }
+
+        mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
+    }
+
     /**
      * The result of checking whether to show the save dialog, when session can be saved.
      *
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159..1a5d91c 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4369,7 +4369,7 @@
             return OperationType.BACKUP;
         }
 
-        long oldCallingId = Binder.clearCallingIdentity();
+        final long oldCallingId = Binder.clearCallingIdentity();
         try {
             IBackupTransport transport = transportClient.connectOrThrow(
                     /* caller */ "BMS.getOperationTypeFromTransport");
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b7feb63..4127556 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -137,6 +137,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -145,6 +147,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
@@ -182,6 +185,11 @@
     private static final String XML_ATTR_TIME_APPROVED = "time_approved";
     private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
 
+    private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    static {
+        sDateFormat.setTimeZone(TimeZone.getDefault());
+    }
+
     private final CompanionDeviceManagerImpl mImpl;
     private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
     private PowerWhitelistManager mPowerWhitelistManager;
@@ -641,7 +649,7 @@
                             association.getDeviceMacAddress(),
                             association.getPackageName(),
                             association.getDeviceProfile(),
-                            active, /* notifyOnDeviceNearby */
+                            active /* notifyOnDeviceNearby */,
                             association.getTimeApprovedMs());
                 } else {
                     return association;
@@ -721,12 +729,40 @@
             synchronized (mLock) {
                 for (UserInfo user : getAllUsers()) {
                     forEach(mCachedAssociations.get(user.id), a -> {
-                        fout.append("  ")
-                                .append("u").append("" + a.getUserId()).append(": ")
-                                .append(a.getPackageName()).append(" - ")
-                                .append(a.getDeviceMacAddress()).append('\n');
+                        fout.append("  ").append(a.toString()).append('\n');
                     });
                 }
+
+            }
+            fout.append("Currently Connected Devices:").append('\n');
+            for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
+                fout.append("  ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
+            }
+
+            fout.append("Devices Last Nearby:").append('\n');
+            for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
+                String device = mDevicesLastNearby.keyAt(i);
+                Date time = mDevicesLastNearby.valueAt(i);
+                fout.append("  ").append(device).append(" -> ")
+                        .append(sDateFormat.format(time)).append('\n');
+            }
+
+            fout.append("Discovery Service State:").append('\n');
+            for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
+                int userId = mServiceConnectors.keyAt(i);
+                fout.append("  ")
+                        .append("u").append(Integer.toString(userId)).append(": ")
+                        .append(Objects.toString(mServiceConnectors.valueAt(i)))
+                        .append('\n');
+            }
+
+            fout.append("Device Listener Services State:").append('\n');
+            for (int i = 0, size = mDeviceListenerServiceConnectors.size(); i < size; i++) {
+                int userId = mDeviceListenerServiceConnectors.keyAt(i);
+                fout.append("  ")
+                        .append("u").append(Integer.toString(userId)).append(": ")
+                        .append(Objects.toString(mDeviceListenerServiceConnectors.valueAt(i)))
+                        .append('\n');
             }
         }
     }
@@ -777,7 +813,7 @@
                         + " for " + association
                         + " - profile still present in " + otherAssociationWithDeviceProfile);
             } else {
-                long identity = Binder.clearCallingIdentity();
+                final long identity = Binder.clearCallingIdentity();
                 try {
                     mRoleManager.removeRoleHolderAsUser(
                             association.getDeviceProfile(),
@@ -905,7 +941,7 @@
                 .getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
 
         Signature[] signatures = mPackageManagerInternal
-                .getPackage(packageName).getSigningDetails().signatures;
+                .getPackage(packageName).getSigningDetails().getSignatures();
         String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
 
         Set<String> sameOemPackageCerts =
@@ -1043,7 +1079,7 @@
     }
 
     private List<UserInfo> getAllUsers() {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             return mUserManager.getUsers();
         } finally {
@@ -1059,7 +1095,7 @@
     }
 
     private Set<Association> getAllAssociations() {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             ArraySet<Association> result = new ArraySet<>();
             for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -1071,6 +1107,7 @@
         }
     }
 
+
     private Set<Association> getAllAssociations(
             int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
         return CollectionUtils.filter(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 55b982b..282b868 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -107,6 +107,7 @@
         ":display-device-config",
         ":display-layout-config",
         ":device-state-config",
+        ":guiconstants_aidl",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
@@ -152,7 +153,7 @@
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.rebootescrow-V1-java",
-        "android.hardware.soundtrigger-V2.3-java",
+        "android.hardware.soundtrigger-V2.4-java",
         "android.hardware.power.stats-V1-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 95186ee..230e380 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager.ComponentInfoFlags;
 import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.SigningDetails.CertCapabilities;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.os.Bundle;
@@ -38,6 +39,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -69,7 +71,6 @@
             PACKAGE_BROWSER,
             PACKAGE_SYSTEM_TEXT_CLASSIFIER,
             PACKAGE_PERMISSION_CONTROLLER,
-            PACKAGE_DOCUMENTER,
             PACKAGE_CONFIGURATOR,
             PACKAGE_INCIDENT_REPORT_APPROVER,
             PACKAGE_APP_PREDICTOR,
@@ -710,6 +711,29 @@
     public abstract boolean filterAppAccess(
             @NonNull String packageName, int callingUid, int userId);
 
+    /**
+     * Returns whether or not access to the application which belongs to the given UID should be
+     * filtered. If the UID is part of a shared user ID, return {@code true} if all applications
+     * belong to the shared user ID should be filtered.
+     *
+     * @see #filterAppAccess(AndroidPackage, int, int)
+     */
+    public abstract boolean filterAppAccess(int uid, int callingUid);
+
+    /**
+     * Fetches all app Ids that a given application is currently visible to the provided user.
+     *
+     * <p>
+     * <strong>Note: </strong>This only includes UIDs >= {@link Process#FIRST_APPLICATION_UID}
+     * as all other UIDs can already see all applications.
+     * </p>
+     *
+     * If the app is visible to all UIDs, null is returned. If the app is not visible to any
+     * applications, the int array will be empty.
+     */
+    @Nullable
+    public abstract int[] getVisibilityAllowList(@NonNull String packageName, int userId);
+
     /** Returns whether the given package was signed by the platform */
     public abstract boolean isPlatformSigned(String pkg);
 
@@ -736,7 +760,7 @@
      * signing history for {@code serverUid} and with the {@code capability} specified.
      */
     public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
-            @PackageParser.SigningDetails.CertCapabilities int capability);
+            @CertCapabilities int capability);
 
     /**
      * Get appIds of all available apps which specified android:sharedUserId in the manifest.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be..b4912bb 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -614,8 +614,12 @@
         }
         // waive WRITE_SECURE_SETTINGS permission check
         final long callingIdentity = Binder.clearCallingIdentity();
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
-        Binder.restoreCallingIdentity(callingIdentity);
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.BLUETOOTH_ON, value);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index ac43fbd..e4a30ad5 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -402,7 +402,7 @@
                 return;
             }
 
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 onSensorUseStarted(uid, packageName, sensor);
             } finally {
@@ -750,7 +750,7 @@
                 }
 
                 if (!enable) {
-                    long token = Binder.clearCallingIdentity();
+                    final long token = Binder.clearCallingIdentity();
                     try {
                         // Remove any notifications prompting the user to disable sensory privacy
                         NotificationManager notificationManager =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c2926..ab6b8a2 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -455,12 +455,6 @@
             "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
 
 
-    /** Automotive device unlockes users before system boot complete and this requires special
-     * handling as vold reset can lead into race conditions. When this is set, all users unlocked
-     * in {@code UserManager} level are unlocked after vold reset.
-     */
-    private final boolean mIsAutomotive;
-
     private VolumeInfo findVolumeByIdOrThrow(String id) {
         synchronized (mLock) {
             final VolumeInfo vol = mVolumes.get(id);
@@ -1133,9 +1127,7 @@
                     mVold.onUserStarted(userId);
                     mStoraged.onUserStarted(userId);
                 }
-                if (mIsAutomotive) {
-                    restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
-                }
+                restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
                 mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
                 mStorageManagerInternal.onReset(mVold);
             } catch (Exception e) {
@@ -1219,13 +1211,11 @@
         // Record user as started so newly mounted volumes kick off events
         // correctly, then synthesize events for any already-mounted volumes.
         synchronized (mLock) {
-            if (mIsAutomotive) {
-                for (int unlockedUser : mSystemUnlockedUsers) {
-                    if (unlockedUser == userId) {
-                        // This can happen as restoreAllUnlockedUsers can double post the message.
-                        Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
-                        return;
-                    }
+            for (int unlockedUser : mSystemUnlockedUsers) {
+                if (unlockedUser == userId) {
+                    // This can happen as restoreAllUnlockedUsers can double post the message.
+                    Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
+                    return;
                 }
             }
             for (int i = 0; i < mVolumes.size(); i++) {
@@ -1551,17 +1541,19 @@
         }
 
         if (vol.type == VolumeInfo.TYPE_EMULATED) {
+            if (!mStorageSessionController.supportsExternalStorage(vol.mountUserId)) {
+                Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+                        + Integer.toString(vol.mountUserId)
+                        + " does not support external storage.");
+                return;
+            }
+
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
 
-            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
-                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
-                Slog.v(TAG, "Found primary storage at " + vol);
-                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
-                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
-                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
-
-            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
+            if ((Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
+                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id))
+                    || Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
                 Slog.v(TAG, "Found primary storage at " + vol);
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
@@ -1922,9 +1914,6 @@
         if (WATCHDOG_ENABLE) {
             Watchdog.getInstance().addMonitor(this);
         }
-
-        mIsAutomotive = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE);
     }
 
     private void start() {
@@ -3461,48 +3450,6 @@
         }
     }
 
-    @Override
-    public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
-            @StorageManager.AppIoBlockedReason int reason) {
-        enforceExternalStorageService();
-
-        mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
-    }
-
-    @Override
-    public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
-            @StorageManager.AppIoBlockedReason int reason) {
-        enforceExternalStorageService();
-
-        mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
-    }
-
-    @Override
-    public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
-            @StorageManager.AppIoBlockedReason int reason) {
-        return isAppIoBlocked(uid);
-    }
-
-
-    private boolean isAppIoBlocked(int uid) {
-        return mStorageSessionController.isAppIoBlocked(uid);
-    }
-
-    /**
-     * Enforces that the caller is the {@link ExternalStorageService}
-     *
-     * @throws SecurityException if the caller doesn't have the
-     * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
-     * {@link ExternalStorageService}
-     */
-    private void enforceExternalStorageService() {
-        enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
-        int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
-        if (callingAppId != mMediaStoreAuthorityAppId) {
-            throw new SecurityException("Only the ExternalStorageService is permitted");
-        }
-    }
-
     /**
      * Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission
      * to launch the manageSpaceActivity of the App specified by packageName.
@@ -3517,7 +3464,7 @@
         // We want to call the manageSpaceActivity as a SystemService and clear identity
         // of the calling App
         int originalUid = Binder.getCallingUidOrThrow();
-        long token = Binder.clearCallingIdentity();
+        final long token = Binder.clearCallingIdentity();
 
         try {
             ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
@@ -3550,6 +3497,46 @@
             Binder.restoreCallingIdentity(token);
         }
     }
+    
+    @Override
+    public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
+        enforceExternalStorageService();
+
+        mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
+    }
+
+    @Override
+    public void notifyAppIoResumed(String volumeUuid, int uid, int tid, int reason) {
+        enforceExternalStorageService();
+
+        mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
+    }
+
+    @Override
+    public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
+            @StorageManager.AppIoBlockedReason int reason) {
+        return isAppIoBlocked(uid);
+    }
+
+
+    private boolean isAppIoBlocked(int uid) {
+        return mStorageSessionController.isAppIoBlocked(uid);
+    }
+
+    /**
+     * Enforces that the caller is the {@link ExternalStorageService}
+     *
+     * @throws SecurityException if the caller doesn't have the
+     * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
+     * {@link ExternalStorageService}
+     */
+    private void enforceExternalStorageService() {
+        enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
+        int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+        if (callingAppId != mMediaStoreAuthorityAppId) {
+            throw new SecurityException("Only the ExternalStorageService is permitted");
+        }
+    }
 
     /** Not thread safe */
     class AppFuseMountScope extends AppFuseBridge.MountScope {
@@ -4599,7 +4586,6 @@
             pw.println();
             pw.println("Local unlocked users: " + mLocalUnlockedUsers);
             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
-            pw.println("isAutomotive:" + mIsAutomotive);
         }
 
         synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a6a8cf01..76b5581 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -60,13 +60,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.RegisteredServicesCache;
 import android.content.pm.RegisteredServicesCacheListener;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails.CertCapabilities;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
 import android.database.sqlite.SQLiteStatement;
 import android.os.Binder;
 import android.os.Bundle;
@@ -1833,6 +1834,11 @@
                                 + ", skipping since the account already exists");
                         return false;
                     }
+                    if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+                                + ", skipping since more than 50 accounts on device exist");
+                        return false;
+                    }
                     long accountId = accounts.accountsDb.insertCeAccount(account, password);
                     if (accountId < 0) {
                         Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
@@ -4871,9 +4877,7 @@
                 int targetUid = targetActivityInfo.applicationInfo.uid;
                 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
                 if (!isExportedSystemActivity(targetActivityInfo)
-                        && !pmi.hasSignatureCapability(
-                                targetUid, authUid,
-                                PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+                        && !pmi.hasSignatureCapability(targetUid, authUid, CertCapabilities.AUTH)) {
                     String pkgName = targetActivityInfo.packageName;
                     String activityName = targetActivityInfo.name;
                     String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -5245,7 +5249,7 @@
                     logStatement.bindLong(6, userDebugDbInsertionPoint);
                     try {
                         logStatement.execute();
-                    } catch (IllegalStateException e) {
+                    } catch (IllegalStateException | SQLiteFullException e) {
                         // Guard against crash, DB can already be closed
                         // since this statement is executed on a handler thread
                         Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
@@ -5626,8 +5630,7 @@
                     return SIGNATURE_CHECK_UID_MATCH;
                 }
                 if (pmi.hasSignatureCapability(
-                        serviceInfo.uid, callingUid,
-                        PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+                        serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
                     return SIGNATURE_CHECK_MATCH;
                 }
             }
@@ -5668,8 +5671,7 @@
         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
                 serviceInfos) {
             if (isOtherwisePermitted || pmi.hasSignatureCapability(
-                    serviceInfo.uid, callingUid,
-                    PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+                    serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
                 managedAccountTypes.add(serviceInfo.type.type);
             }
         }
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index 8dcc04a..df1b4f4 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -1,9 +1 @@
-carlosvaldivia@google.com
-dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
-yamasani@google.com
-omakoto@google.com
-
+include /core/java/android/accounts/OWNERS
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 99ae52c..6129100 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3290,13 +3290,19 @@
 
         final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
         final long now = System.currentTimeMillis();
-        Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
-        for (int i = 0; i < files.length; ++i) {
-            if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
-                if (!files[i].delete()) {
-                    Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+        try {
+            Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+            for (int i = 0; i < files.length; ++i) {
+                if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+                    if (!files[i].delete()) {
+                        Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+                    }
                 }
             }
+        } catch (IllegalArgumentException e) {
+            // The modification times changed while we were sorting. Bail...
+            // https://issuetracker.google.com/169836837
+            Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
         }
     }
 
@@ -3349,7 +3355,7 @@
         // control of all writes to the file in question.
 
         // We must complete all stack dumps within 20 seconds.
-        long remainingTime = 20 * 1000;
+        long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
         // As applications are usually interested with the ANR stack traces, but we can't share with
         // them the stack traces other than their own stacks. So after the very first PID is
@@ -3452,30 +3458,39 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
+            boolean permitted = true;
             // Instant packages are not protected
             if (getPackageManagerInternal().isPackageDataProtected(
                     resolvedUserId, packageName)) {
-                throw new SecurityException(
-                        "Cannot clear data for a protected package: " + packageName);
+                if (ActivityManager.checkUidPermission(android.Manifest.permission.MANAGE_USERS,
+                        uid) == PERMISSION_GRANTED) {
+                    // The caller has the MANAGE_USERS permission, tell them what's going on.
+                    throw new SecurityException(
+                            "Cannot clear data for a protected package: " + packageName);
+                } else {
+                    permitted = false; // fall through and throw the SecurityException below.
+                }
             }
 
             ApplicationInfo applicationInfo = null;
-            try {
-                applicationInfo = pm.getApplicationInfo(packageName,
-                        MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
-            } catch (RemoteException e) {
-                /* ignore */
+            if (permitted) {
+                try {
+                    applicationInfo = pm.getApplicationInfo(packageName,
+                            MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                permitted = (applicationInfo != null && applicationInfo.uid == uid) // own uid data
+                        || (checkComponentPermission(permission.CLEAR_APP_USER_DATA,
+                                pid, uid, -1, true) == PackageManager.PERMISSION_GRANTED);
             }
-            appInfo = applicationInfo;
 
-            final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
-
-            if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
-                        pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+            if (!permitted) {
                 throw new SecurityException("PID " + pid + " does not have permission "
                         + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
                         + " of package " + packageName);
             }
+            appInfo = applicationInfo;
 
             final boolean hasInstantMetadata = getPackageManagerInternal()
                     .hasInstantApplicationMetadata(packageName, resolvedUserId);
@@ -3521,15 +3536,17 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                     intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
+                    final int[] visibilityAllowList =
+                            mPackageManagerInt.getVisibilityAllowList(packageName, resolvedUserId);
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
                                 null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
-                                false, false, resolvedUserId, false, null);
+                                false, false, resolvedUserId, false, null, visibilityAllowList);
                     } else {
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
                                 null, null, 0, null, null, null, null, false, false, resolvedUserId,
-                                false, null);
+                                false, null, visibilityAllowList);
                     }
 
                     if (observer != null) {
@@ -4110,11 +4127,8 @@
             return;
         }
         if (appId < 0) {
-            try {
-                appId = UserHandle.getAppId(AppGlobals.getPackageManager()
-                        .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
-            } catch (RemoteException e) {
-            }
+            appId = UserHandle.getAppId(getPackageManagerInternal().getPackageUid(packageName,
+                    MATCH_DEBUG_TRIAGED_MISSING | MATCH_ANY_USER, UserHandle.USER_SYSTEM));
         }
 
         mProcessList.killAppZygotesLocked(packageName, appId, userId, true /* force */);
@@ -4131,11 +4145,8 @@
         }
 
         if (appId < 0 && packageName != null) {
-            try {
-                appId = UserHandle.getAppId(AppGlobals.getPackageManager()
-                        .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
-            } catch (RemoteException e) {
-            }
+            appId = UserHandle.getAppId(getPackageManagerInternal().getPackageUid(packageName,
+                    MATCH_DEBUG_TRIAGED_MISSING | MATCH_ANY_USER, UserHandle.USER_SYSTEM));
         }
 
         boolean didSomething;
@@ -5684,6 +5695,11 @@
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
+        if (uid != ROOT_UID) { // bypass the root
+            if (mPackageManagerInt.filterAppAccess(uid, Binder.getCallingUid())) {
+                return PackageManager.PERMISSION_DENIED;
+            }
+        }
         return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags)
                 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
@@ -8332,11 +8348,13 @@
                 if (lines > 0) {
                     sb.append("\n");
 
-                    // Merge several logcat streams, and take the last N lines
                     InputStreamReader input = null;
                     try {
                         java.lang.Process logcat = new ProcessBuilder(
-                                "/system/bin/timeout", "-k", "15s", "10s",
+                                // Time out after 10s, but kill logcat with SEGV
+                                // so we can investigate why it didn't finish.
+                                "/system/bin/timeout", "-s", "SEGV", "10s",
+                                // Merge several logcat streams, and take the last N lines.
                                 "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
                                 "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
                                         .redirectErrorStream(true).start();
@@ -8379,9 +8397,13 @@
         // assume our apps are happy - lazy create the list
         final List<ActivityManager.ProcessErrorStateInfo>[] errList = new List[1];
 
+        final int callingUid = Binder.getCallingUid();
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
-                Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
-        int userId = UserHandle.getUserId(Binder.getCallingUid());
+                callingUid) == PackageManager.PERMISSION_GRANTED;
+        int userId = UserHandle.getUserId(callingUid);
+
+        final boolean hasDumpPermission = ActivityManager.checkUidPermission(
+                android.Manifest.permission.DUMP, callingUid) == PackageManager.PERMISSION_GRANTED;
 
         synchronized (mProcLock) {
             // iterate across all processes
@@ -8389,6 +8411,9 @@
                 if (!allUsers && app.userId != userId) {
                     return;
                 }
+                if (!hasDumpPermission && app.info.uid != callingUid) {
+                    return;
+                }
                 final ProcessErrorStateRecord errState = app.mErrorState;
                 final boolean crashing = errState.isCrashing();
                 final boolean notResponding = errState.isNotResponding();
@@ -12075,6 +12100,31 @@
             Slog.w(TAG, "Unable to bind backup agent for " + packageName);
             return false;
         }
+        if (app.backupAgentName != null) {
+            final ComponentName backupAgentName = new ComponentName(
+                    app.packageName, app.backupAgentName);
+            int enableState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+            try {
+                enableState = pm.getComponentEnabledSetting(backupAgentName, instantiatedUserId);
+            } catch (RemoteException e) {
+                // can't happen; package manager is process-local
+            }
+            switch (enableState) {
+                case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+                case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+                case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                    Slog.w(TAG, "Unable to bind backup agent for " + backupAgentName
+                            + ", the backup agent component is disabled.");
+                    return false;
+
+                case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                default:
+                    // Since there's no way to declare a backup agent disabled in the manifest,
+                    // assume the case COMPONENT_ENABLED_STATE_DEFAULT to be enabled.
+                    break;
+            }
+        }
 
         int oldBackupUid;
         int newBackupUid;
@@ -13599,7 +13649,8 @@
             IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
             int userId, boolean allowBackgroundActivityStarts,
-            @Nullable IBinder backgroundActivityStartsToken) {
+            @Nullable IBinder backgroundActivityStartsToken,
+            @Nullable int[] broadcastAllowList) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
@@ -13611,8 +13662,7 @@
                         resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
                         OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                         realCallingPid, userId, allowBackgroundActivityStarts,
-                        backgroundActivityStartsToken,
-                        null /* broadcastAllowList */);
+                        backgroundActivityStartsToken, broadcastAllowList);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -15797,13 +15847,14 @@
                 IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
                 String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
                 int userId, boolean allowBackgroundActivityStarts,
-                @Nullable IBinder backgroundActivityStartsToken) {
+                @Nullable IBinder backgroundActivityStartsToken,
+                @Nullable int[] broadcastAllowList) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
                         uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
                         resultCode, resultData, resultExtras, requiredPermission, bOptions,
                         serialized, sticky, userId, allowBackgroundActivityStarts,
-                        backgroundActivityStartsToken);
+                        backgroundActivityStartsToken, broadcastAllowList);
             }
         }
 
@@ -16348,6 +16399,15 @@
                 return mProcessList.getIsolatedProcessesLocked(uid);
             }
         }
+
+        /** @see ActivityManagerService#sendIntentSender */
+        @Override
+        public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
+                Intent intent, String resolvedType,
+                IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+            return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
+                    intent, resolvedType, finishedReceiver, requiredPermission, options);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 0c633ca..449f02e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -79,6 +79,9 @@
     // There is some accuracy error in wifi reports so allow some slop in the results.
     private static final long MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS = 750;
 
+    // Delay for clearing out battery stats for UIDs corresponding to a removed user
+    public static final int UID_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS = 10_000;
+
     private final ScheduledExecutorService mExecutorService =
             Executors.newSingleThreadScheduledExecutor(
                     (ThreadFactory) r -> {
@@ -346,6 +349,17 @@
         }
     }
 
+    @Override
+    public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
+        synchronized (BatteryExternalStatsWorker.this) {
+            return mExecutorService.schedule(() -> {
+                synchronized (mStats) {
+                    mStats.clearRemovedUserUidsLocked(userId);
+                }
+            }, UID_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
+        }
+    }
+
     /**
      * Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a
      * new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0).
@@ -481,7 +495,7 @@
                         mStats.removeIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
                                 SystemClock.uptimeMillis());
                     }
-                    mStats.clearPendingRemovedUids();
+                    mStats.clearPendingRemovedUidsLocked();
                 }
             } catch (Exception e) {
                 Slog.wtf(TAG, "Error updating external stats: ", e);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9f41c8b..399a245 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -492,9 +492,9 @@
         final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
         powerMgr.registerLowPowerModeObserver(this);
         synchronized (mStats) {
-            mStats.notePowerSaveModeLocked(
+            mStats.notePowerSaveModeLockedInit(
                     powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
-                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), true);
+                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
         }
         (new WakeupReasonThread()).start();
     }
@@ -537,7 +537,7 @@
             mHandler.post(() -> {
                 synchronized (mStats) {
                     mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
-                            elapsedRealtime, uptime, false);
+                            elapsedRealtime, uptime);
                 }
             });
         }
@@ -1978,7 +1978,7 @@
     }
 
     @Override
-    public void noteResetBleScan() {
+    public void noteBleScanReset() {
         enforceCallingPermission();
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 50278fd..1ead7e3 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -38,16 +38,25 @@
     private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
-    @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+    @VisibleForTesting
+    static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+    @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
+            "oom_re_ranking_preserve_top_n_apps";
+    @VisibleForTesting
+    static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
 
     private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
             new ScoreComparator();
@@ -66,15 +75,21 @@
 
     @GuardedBy("mPhenotypeFlagLock")
     private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
     // Weight to apply to the LRU ordering.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
+    @VisibleForTesting
+    float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
     // Weight to apply to the ordering by number of times the process has been added to the cache.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
+    @VisibleForTesting
+    float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
     // Weight to apply to the ordering by RSS used by the processes.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
+    @VisibleForTesting
+    float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
 
     // Positions to replace in the lru list.
     @GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +108,8 @@
                                 updateUseOomReranking();
                             } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
                                 updateNumberToReRank();
+                            } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
+                                updatePreserveTopNApps();
                             } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
                                 updateLruWeight();
                             } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -160,6 +177,19 @@
     }
 
     @GuardedBy("mPhenotypeFlagLock")
+    private void updatePreserveTopNApps() {
+        int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
+        if (preserveTopNApps < 0) {
+            Slog.w(OomAdjuster.TAG,
+                    "Found negative value for preserveTopNApps, setting to default: "
+                            + preserveTopNApps);
+            preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+        }
+        mPreserveTopNApps = preserveTopNApps;
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
     private void updateLruWeight() {
         mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -183,6 +213,33 @@
      */
     @GuardedBy({"mService", "mProcLock"})
     void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
+        // The lruList is a list of processes ordered by how recently they were used. The
+        // least-recently-used apps are at the beginning of the list. We keep track of two
+        // indices in the lruList:
+        //
+        // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
+        // lruList=
+        //   0: app A       ^
+        //   1: app B       | These apps are re-ranked, as they are the first five apps (see
+        //   2: app C       | getNumberToReRank), excluding...
+        //   3: app D       v
+        //   4: app E       ^
+        //   5: app F       | The three most-recently-used apps in the cache (see preserveTopNApps).
+        //   6: app G       v
+        //   7: service A   ^
+        //   8: service B   | Everything beyond lruProcessServiceStart is ignored, as these aren't
+        //   9: service C   | apps.
+        //  10: activity A  |
+        //      ...         |
+        //
+        // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
+        // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
+        // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
+        // happens.
+        //
+        // Note that some apps in the `lruList` can be skipped, if they don't pass
+        //`appCanBeReRanked`.
+
         float lruWeight;
         float usesWeight;
         float rssWeight;
@@ -202,52 +259,67 @@
             return;
         }
 
+        int numProcessesEvaluated = 0;
         // Collect the least recently used processes to re-rank, only rank cached
         // processes further down the list than mLruProcessServiceStart.
-        int cachedProcessPos = 0;
-        for (int i = 0; i < lruProcessServiceStart
-                && cachedProcessPos < scoredProcessRecords.length; ++i) {
-            ProcessRecord app = lruList.get(i);
+        int numProcessesReRanked = 0;
+        while (numProcessesEvaluated < lruProcessServiceStart
+                && numProcessesReRanked < scoredProcessRecords.length) {
+            ProcessRecord process = lruList.get(numProcessesEvaluated);
             // Processes that will be assigned a cached oom adj score.
-            if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
-                    >= ProcessList.UNKNOWN_ADJ) {
-                scoredProcessRecords[cachedProcessPos].proc = app;
-                scoredProcessRecords[cachedProcessPos].score = 0.0f;
-                lruPositions[cachedProcessPos] = i;
-                ++cachedProcessPos;
+            if (appCanBeReRanked(process)) {
+                scoredProcessRecords[numProcessesReRanked].proc = process;
+                scoredProcessRecords[numProcessesReRanked].score = 0.0f;
+                lruPositions[numProcessesReRanked] = numProcessesEvaluated;
+                ++numProcessesReRanked;
             }
+            ++numProcessesEvaluated;
         }
 
-        // TODO maybe ensure a certain number above this in the cache before re-ranking.
-        if (cachedProcessPos < scoredProcessRecords.length)  {
-            // Ignore we don't have enough processes to worry about re-ranking.
-            return;
+        // Count how many apps we're not re-ranking (up to mPreserveTopNApps).
+        int numProcessesNotReRanked = 0;
+        while (numProcessesEvaluated < lruProcessServiceStart
+                && numProcessesNotReRanked < mPreserveTopNApps) {
+            ProcessRecord process = lruList.get(numProcessesEvaluated);
+            if (appCanBeReRanked(process)) {
+                numProcessesNotReRanked++;
+            }
+            numProcessesEvaluated++;
+        }
+        // Exclude the top `mPreserveTopNApps` apps from re-ranking.
+        if (numProcessesNotReRanked < mPreserveTopNApps) {
+            numProcessesReRanked -= mPreserveTopNApps - numProcessesNotReRanked;
+            if (numProcessesReRanked < 0) {
+                numProcessesReRanked = 0;
+            }
         }
 
         // Add scores for each of the weighted features we want to rank based on.
         if (lruWeight > 0.0f) {
             // This doesn't use the LRU list ordering as after the first re-ranking
             // that will no longer be lru.
-            Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
+            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+                    LAST_ACTIVITY_TIME_COMPARATOR);
             addToScore(scoredProcessRecords, lruWeight);
         }
         if (rssWeight > 0.0f) {
             synchronized (mService.mAppProfiler.mProfilerLock) {
-                Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+                Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
             }
             addToScore(scoredProcessRecords, rssWeight);
         }
         if (usesWeight > 0.0f) {
-            Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
+            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
             addToScore(scoredProcessRecords, usesWeight);
         }
 
         // Re-rank by the new combined score.
-        Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
+        Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+                SCORED_PROCESS_RECORD_COMPARATOR);
 
         if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
             boolean printedHeader = false;
-            for (int i = 0; i < scoredProcessRecords.length; ++i) {
+            for (int i = 0; i < numProcessesReRanked; ++i) {
                 if (scoredProcessRecords[i].proc.getPid()
                         != lruList.get(lruPositions[i]).getPid()) {
                     if (!printedHeader) {
@@ -260,12 +332,18 @@
             }
         }
 
-        for (int i = 0; i < scoredProcessRecords.length; ++i) {
+        for (int i = 0; i < numProcessesReRanked; ++i) {
             lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
             scoredProcessRecords[i].proc = null;
         }
     }
 
+    private static boolean appCanBeReRanked(ProcessRecord process) {
+        return !process.isKilledByAm()
+                && process.getThread() != null
+                && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
+    }
+
     private static void addToScore(RankedProcessRecord[] scores, float weight) {
         for (int i = 1; i < scores.length; ++i) {
             scores[i].score += i * weight;
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 5c9d385..2e3e635 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -94,8 +94,6 @@
         sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.ANGLE_ALLOWLIST, String.class);
-        sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_EGL_FEATURES, String.class);
         sGlobalSettingToTypeMap.put(
                 Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d6bf8db..93d5c06b 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -256,7 +256,7 @@
     @GuardedBy("mService")
     private boolean mPendingFullOomAdjUpdate = false;
 
-    private final PlatformCompatCache mPlatformCompatCache;
+    final PlatformCompatCache mPlatformCompatCache;
 
     /** Overrideable by a test */
     @VisibleForTesting
@@ -309,6 +309,12 @@
             }
         }
 
+        void onApplicationInfoChanged(ApplicationInfo app) {
+            for (int i = mCaches.size() - 1; i >= 0; i--) {
+                mCaches.valueAt(i).onApplicationInfoChanged(app);
+            }
+        }
+
         static class CacheItem implements CompatChange.ChangeListener {
             private final PlatformCompat mPlatformCompat;
             private final long mChangeId;
@@ -328,22 +334,14 @@
                     final int index = mCache.indexOfKey(app.packageName);
                     Pair<Boolean, WeakReference<ApplicationInfo>> p;
                     if (index < 0) {
-                        p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
-                                                                                        app),
-                                new WeakReference<>(app));
-                        mCache.put(app.packageName, p);
-                        return p.first;
+                        return fetchLocked(app, index);
                     }
                     p = mCache.valueAt(index);
                     if (p.second.get() == app) {
                         return p.first;
                     }
                     // Cache is invalid, regenerate it
-                    p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
-                                                                                    app),
-                            new WeakReference<>(app));
-                    mCache.setValueAt(index, p);
-                    return p.first;
+                    return fetchLocked(app, index);
                 }
             }
 
@@ -353,10 +351,40 @@
                 }
             }
 
+            @GuardedBy("mLock")
+            boolean fetchLocked(ApplicationInfo app, int index) {
+                final Pair<Boolean, WeakReference<ApplicationInfo>> p = new Pair<>(
+                        mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId, app),
+                        new WeakReference<>(app));
+                if (index >= 0) {
+                    mCache.setValueAt(index, p);
+                } else {
+                    mCache.put(app.packageName, p);
+                }
+                return p.first;
+            }
+
+            void onApplicationInfoChanged(ApplicationInfo app) {
+                synchronized (mLock) {
+                    final int index = mCache.indexOfKey(app.packageName);
+                    if (index >= 0) {
+                        fetchLocked(app, index);
+                    }
+                }
+            }
+
             @Override
             public void onCompatChange(String packageName) {
                 synchronized (mLock) {
-                    mCache.remove(packageName);
+                    final int index = mCache.indexOfKey(packageName);
+                    if (index >= 0) {
+                        final ApplicationInfo app = mCache.valueAt(index).second.get();
+                        if (app != null) {
+                            fetchLocked(app, index);
+                        } else {
+                            mCache.removeAt(index);
+                        }
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index dc924c1..175da9c 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -481,7 +481,8 @@
                                 key.featureId, uid, callingUid, callingPid, finalIntent,
                                 resolvedType, finishedReceiver, code, null, null,
                                 requiredPermission, options, (finishedReceiver != null), false,
-                                userId, allowedByToken || allowTrampoline, bgStartsToken);
+                                userId, allowedByToken || allowTrampoline, bgStartsToken,
+                                null /* broadcastAllowList */);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b77270f..a97738f 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4710,6 +4710,8 @@
                         if (ai != null) {
                             if (ai.packageName.equals(app.info.packageName)) {
                                 app.info = ai;
+                                mService.mOomAdjuster.mPlatformCompatCache
+                                        .onApplicationInfoChanged(ai);
                             }
                             app.getThread().scheduleApplicationInfoChanged(ai);
                             targetProcesses.add(app.getWindowProcessController());
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e022e97..c103344 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -92,6 +92,8 @@
         DeviceConfig.NAMESPACE_STATSD_NATIVE,
         DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
         DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
     };
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba3e1fb..fa7eae3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -425,6 +425,7 @@
     }
 
     @GuardedBy("mLock")
+    @VisibleForTesting
     List<Integer> getRunningUsersLU() {
         ArrayList<Integer> runningUsers = new ArrayList<>();
         for (Integer userId : mUserLru) {
@@ -450,7 +451,7 @@
     }
 
     @GuardedBy("mLock")
-    void stopRunningUsersLU(int maxRunningUsers) {
+    private void stopRunningUsersLU(int maxRunningUsers) {
         List<Integer> currentlyRunning = getRunningUsersLU();
         Iterator<Integer> iterator = currentlyRunning.iterator();
         while (currentlyRunning.size() > maxRunningUsers && iterator.hasNext()) {
@@ -590,7 +591,11 @@
                 Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
                 return;
             }
+
+            final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+            t.traceBegin("UM.onBeforeUnlockUser-" + userId);
             mInjector.getUserManager().onBeforeUnlockUser(userId);
+            t.traceEnd();
             synchronized (mLock) {
                 // Do not proceed if unexpected state
                 if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
@@ -612,7 +617,7 @@
      * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
      * {@link UserState#STATE_RUNNING_UNLOCKED}.
      */
-    void finishUserUnlocked(final UserState uss) {
+    private void finishUserUnlocked(final UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
         EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
         // Only keep marching forward if user is actually unlocked
@@ -907,7 +912,7 @@
     private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
             final IStopUserCallback stopUserCallback,
             KeyEvictedCallback keyEvictedCallback) {
-        if (DEBUG_MU) Slogf.i(TAG, "stopSingleUserLocked userId=" + userId);
+        Slogf.i(TAG, "stopSingleUserLU userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
         if (uss == null) {  // User is not started
             // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
@@ -1000,7 +1005,7 @@
         }
     }
 
-    void finishUserStopping(final int userId, final UserState uss,
+    private void finishUserStopping(final int userId, final UserState uss,
             final boolean allowDelayedLocking) {
         EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
         synchronized (mLock) {
@@ -1041,6 +1046,7 @@
                 Binder.getCallingPid(), userId);
     }
 
+    @VisibleForTesting
     void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
         final int userId = uss.mHandle.getIdentifier();
         if (DEBUG_MU) {
@@ -1264,7 +1270,7 @@
         });
     }
 
-    void startProfiles() {
+    private void startProfiles() {
         int currentUserId = getCurrentUserId();
         if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
         List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
@@ -1317,6 +1323,7 @@
         return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null);
     }
 
+    @VisibleForTesting
     boolean startUser(final @UserIdInt int userId, final boolean foreground) {
         return startUser(userId, foreground, null);
     }
@@ -1683,7 +1690,11 @@
             return false;
         }
 
-        if (!finishUserUnlocking(uss)) {
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("finishUserUnlocking-" + userId);
+        final boolean finishUserUnlockingResult = finishUserUnlocking(uss);
+        t.traceEnd();
+        if (!finishUserUnlockingResult) {
             notifyFinished(userId, listener);
             return false;
         }
@@ -1775,6 +1786,7 @@
     }
 
     /** Called on handler thread */
+    @VisibleForTesting
     void dispatchUserSwitchComplete(@UserIdInt int userId) {
         mInjector.getWindowManager().setSwitchingUser(false);
         final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1848,7 +1860,11 @@
         }
     }
 
+    @VisibleForTesting
     void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
+
         EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
 
         final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1901,27 +1917,36 @@
             }
         }
         mUserSwitchObservers.finishBroadcast();
+        t.traceEnd(); // end dispatchUserSwitch-
     }
 
     @GuardedBy("mLock")
-    void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
+    private void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
         mCurWaitingUserSwitchCallbacks = null;
         mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
         mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
                 oldUserId, newUserId, uss));
     }
 
+    @VisibleForTesting
     void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("continueUserSwitch-" + oldUserId + "-to-" + newUserId);
+
         EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
 
         if (isUserSwitchUiEnabled()) {
+            t.traceBegin("stopFreezingScreen");
             mInjector.getWindowManager().stopFreezingScreen();
+            t.traceEnd();
         }
         uss.switching = false;
         mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
         mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
         stopGuestOrEphemeralUserIfBackground(oldUserId);
         stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+
+        t.traceEnd(); // end continueUserSwitch
     }
 
     private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
@@ -2354,7 +2379,7 @@
     }
 
     @GuardedBy("mLock")
-    UserInfo getCurrentUserLU() {
+    private UserInfo getCurrentUserLU() {
         int userId = getCurrentOrTargetUserIdLU();
         return getUserInfo(userId);
     }
@@ -2366,12 +2391,12 @@
     }
 
     @GuardedBy("mLock")
-    int getCurrentOrTargetUserIdLU() {
+    private int getCurrentOrTargetUserIdLU() {
         return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
     }
 
     @GuardedBy("mLock")
-    int getCurrentUserIdLU() {
+    private int getCurrentUserIdLU() {
         return mCurrentUserId;
     }
 
@@ -2670,7 +2695,11 @@
                         USER_LIFECYCLE_EVENT_STATE_FINISH);
                 logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
                         USER_LIFECYCLE_EVENT_STATE_BEGIN);
+
+                final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+                t.traceBegin("finishUserUnlocked-" + userId);
                 finishUserUnlocked((UserState) msg.obj);
+                t.traceEnd();
                 break;
             case USER_UNLOCKED_MSG:
                 mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index af8d7a6..3003c52 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -272,6 +272,13 @@
                 "com.android.graphics.intervention.wm.allowDownscale";
 
         /**
+         * Metadata that can be included in the app manifest to allow/disallow any ANGLE
+         * interventions. Default value is TRUE.
+         */
+        public static final String METADATA_ANGLE_ALLOW_ANGLE =
+                "com.android.graphics.intervention.angle.allowAngle";
+
+        /**
          * Metadata that needs to be included in the app manifest to OPT-IN to PERFORMANCE mode.
          * This means the app will assume full responsibility for the experience provided by this
          * mode and the system will enable no window manager downscaling.
@@ -294,6 +301,7 @@
         private boolean mPerfModeOptedIn;
         private boolean mBatteryModeOptedIn;
         private boolean mAllowDownscale;
+        private boolean mAllowAngle;
 
         GamePackageConfiguration(String packageName, int userId) {
             mPackageName = packageName;
@@ -305,10 +313,12 @@
                     mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
                     mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
                     mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
+                    mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
                 } else {
                     mPerfModeOptedIn = false;
                     mBatteryModeOptedIn = false;
                     mAllowDownscale = true;
+                    mAllowAngle = true;
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 // Not all packages are installed, hence ignore those that are not installed yet.
@@ -340,14 +350,26 @@
             public static final String MODE_KEY = "mode";
             public static final String SCALING_KEY = "downscaleFactor";
             public static final String DEFAULT_SCALING = "1.0";
+            public static final String ANGLE_KEY = "useAngle";
 
             private final @GameMode int mGameMode;
             private final String mScaling;
+            private final boolean mUseAngle;
 
             GameModeConfiguration(KeyValueListParser parser) {
                 mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
-                mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode)
+                // isGameModeOptedIn() returns if an app will handle all of the changes necessary
+                // for a particular game mode. If so, the Android framework (i.e.
+                // GameManagerService) will not do anything for the app (like window scaling or
+                // using ANGLE).
+                mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
                         ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+                // We only want to use ANGLE if:
+                // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
+                // - The app has not opted in to performing the work itself AND
+                // - The Phenotype config has enabled it.
+                mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
+                        && parser.getBoolean(ANGLE_KEY, false);
             }
 
             public int getGameMode() {
@@ -358,6 +380,10 @@
                 return mScaling;
             }
 
+            public boolean getUseAngle() {
+                return mUseAngle;
+            }
+
             public boolean isValid() {
                 return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
                         || mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -368,7 +394,8 @@
              * @hide
              */
             public String toString() {
-                return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
+                return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:"
+                        + mUseAngle + "]";
             }
 
             /**
@@ -384,13 +411,14 @@
         }
 
         /**
-         * Gets whether a package has opted into a game mode via its manifest.
+         * Returns if the app will assume full responsibility for the experience provided by this
+         * mode. If True, the system will not perform any interventions for the app.
          *
          * @return True if the app package has specified in its metadata either:
          * "com.android.app.gamemode.performance.enabled" or
          * "com.android.app.gamemode.battery.enabled" with a value of "true"
          */
-        public boolean isGameModeOptedIn(@GameMode int gameMode) {
+        public boolean willGamePerformOptimizations(@GameMode int gameMode) {
             return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY)
                     || (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE);
         }
@@ -631,7 +659,34 @@
                 mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
             }
         }
-        updateCompatModeDownscale(packageName, gameMode);
+        updateInterventions(packageName, gameMode);
+    }
+
+    /**
+     * Get if ANGLE is enabled for the package for the currently enabled game mode.
+     * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+    public @GameMode boolean getAngleEnabled(String packageName, int userId)
+            throws SecurityException {
+        final int gameMode = getGameMode(packageName, userId);
+        if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+            return false;
+        }
+
+        synchronized (mDeviceConfigLock) {
+            final GamePackageConfiguration config = mConfigs.get(packageName);
+            if (config == null) {
+                return false;
+            }
+            GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+                    config.getGameModeConfiguration(gameMode);
+            if (gameModeConfiguration == null) {
+                return false;
+            }
+            return gameModeConfiguration.getUseAngle();
+        }
     }
 
     /**
@@ -753,7 +808,7 @@
             if (DEBUG) {
                 Slog.v(TAG, dumpDeviceConfigs());
             }
-            if (packageConfig.isGameModeOptedIn(gameMode)) {
+            if (packageConfig.willGamePerformOptimizations(gameMode)) {
                 disableCompatScale(packageName);
                 return;
             }
@@ -782,6 +837,17 @@
         return (bitField & modeToBitmask(gameMode)) != 0;
     }
 
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    private void updateUseAngle(String packageName, @GameMode int gameMode) {
+        // TODO (b/188475576): Nothing to do yet. Remove if it's still empty when we're ready to
+        // ship.
+    }
+
+    private void updateInterventions(String packageName, @GameMode int gameMode) {
+        updateCompatModeDownscale(packageName, gameMode);
+        updateUseAngle(packageName, gameMode);
+    }
+
     /**
      * @hide
      */
@@ -839,11 +905,11 @@
                     if (newGameMode != gameMode) {
                         setGameMode(packageName, newGameMode, userId);
                     }
-                    updateCompatModeDownscale(packageName, gameMode);
+                    updateInterventions(packageName, gameMode);
                 }
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Failed to update compat modes for user: " + userId);
+            Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
         }
     }
 
@@ -851,7 +917,7 @@
         final List<PackageInfo> packages =
                 mPackageManager.getInstalledPackagesAsUser(0, userId);
         return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
-                        == ApplicationInfo.CATEGORY_GAME)
+                == ApplicationInfo.CATEGORY_GAME)
                 .map(e -> e.packageName)
                 .toArray(String[]::new);
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a9905dc..ca844f5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1821,13 +1821,13 @@
                     for (int attributionNum = 0; attributionNum < numAttributions;
                             attributionNum++) {
                         ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
-                        attributionTags.add(attribution.tag);
+                        attributionTags.add(attribution.getTag());
 
-                        int numInheritFrom = attribution.inheritFrom.size();
+                        int numInheritFrom = attribution.getInheritFrom().size();
                         for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
                                 inheritFromNum++) {
-                            dstAttributionTags.put(attribution.inheritFrom.get(inheritFromNum),
-                                    attribution.tag);
+                            dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
+                                    attribution.getTag());
                         }
                     }
                 }
@@ -4621,7 +4621,7 @@
         if (pkg.getAttributions() != null) {
             int numAttributions = pkg.getAttributions().size();
             for (int i = 0; i < numAttributions; i++) {
-                if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+                if (pkg.getAttributions().get(i).getTag().equals(attributionTag)) {
                     return true;
                 }
             }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f7d0914..fcd198e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -64,6 +64,7 @@
 import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.hdmi.HdmiAudioSystemClient;
+import android.hardware.hdmi.HdmiClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiTvClient;
@@ -585,11 +586,12 @@
     Set<Integer> mFixedVolumeDevices = new HashSet<>(Arrays.asList(
             AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
-            AudioSystem.DEVICE_OUT_HDMI_ARC,
-            AudioSystem.DEVICE_OUT_HDMI_EARC,
             AudioSystem.DEVICE_OUT_AUX_LINE));
     // Devices for which the volume is always max, no volume panel
-    Set<Integer> mFullVolumeDevices = new HashSet<>();
+    Set<Integer> mFullVolumeDevices = new HashSet<>(Arrays.asList(
+            AudioSystem.DEVICE_OUT_HDMI_ARC,
+            AudioSystem.DEVICE_OUT_HDMI_EARC
+    ));
     // Devices for the which use the "absolute volume" concept (framework sends audio signal
     // full scale, and volume control separately) and can be used for multiple use cases reflected
     // by the audio mode (e.g. media playback in MODE_NORMAL, and phone calls in MODE_IN_CALL).
@@ -2306,7 +2308,6 @@
         if (DEBUG_VOL) {
             Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
         }
-        setSystemAudioMute(masterMute);
         AudioSystem.setMasterMute(masterMute);
         broadcastMasterMuteStatus(masterMute);
 
@@ -2608,14 +2609,6 @@
         }
     }
 
-    /** @see AudioManager#adjustVolume(int, int) */
-    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
-            String callingPackage, String caller) {
-        adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
-                caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
-                VOL_ADJUST_NORMAL);
-    }
-
     public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
         mNavigationRepeatSoundEffectsEnabled = enabled;
     }
@@ -2638,6 +2631,7 @@
         return mHomeSoundEffectEnabled;
     }
 
+    /** All callers come from platform apps/system server, so no attribution tag is needed */
     private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
             int keyEventMode) {
@@ -2713,7 +2707,7 @@
         }
 
         adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
-                hasModifyAudioSettings, keyEventMode);
+                null, hasModifyAudioSettings, keyEventMode);
     }
 
     private boolean notifyExternalVolumeController(int direction) {
@@ -2731,10 +2725,16 @@
         return true;
     }
 
-    /** @see AudioManager#adjustStreamVolume(int, int, int)
-     * Part of service interface, check permissions here */
+    /** Retain API for unsupported app usage */
     public void adjustStreamVolume(int streamType, int direction, int flags,
             String callingPackage) {
+        adjustStreamVolumeWithAttribution(streamType, direction, flags, callingPackage, null);
+    }
+
+    /** @see AudioManager#adjustStreamVolume(int, int, int)
+     * Part of service interface, check permissions here */
+    public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+            String callingPackage, String attributionTag) {
         if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
             Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
                     + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -2744,13 +2744,13 @@
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
                 direction/*val1*/, flags/*val2*/, callingPackage));
         adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+                Binder.getCallingUid(), attributionTag, callingHasAudioSettingsPermission(),
                 VOL_ADJUST_NORMAL);
     }
 
     protected void adjustStreamVolume(int streamType, int direction, int flags,
-            String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
-            int keyEventMode) {
+            String callingPackage, String caller, int uid, String attributionTag,
+            boolean hasModifyAudioSettings, int keyEventMode) {
         if (mUseFixedVolume) {
             return;
         }
@@ -2815,8 +2815,8 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -2888,9 +2888,6 @@
                 } else {
                     state = direction == AudioManager.ADJUST_MUTE;
                 }
-                if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
-                    setSystemAudioMute(state);
-                }
                 for (int stream = 0; stream < mStreamStates.length; stream++) {
                     if (streamTypeAlias == mStreamVolumeAlias[stream]) {
                         if (!(readCameraSoundForced()
@@ -2956,11 +2953,6 @@
                     mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
                 }
             }
-
-            // Check if volume update should be sent to Hdmi system audio.
-            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
-                setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
-            }
         }
 
         final int newIndex = mStreamStates[streamType].getIndex(device);
@@ -2968,7 +2960,13 @@
         if (adjustVolume) {
             synchronized (mHdmiClientLock) {
                 if (mHdmiManager != null) {
-                    if (mHdmiPlaybackClient != null
+                    // At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null
+                    HdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;
+                    if (mHdmiTvClient != null) {
+                        fullVolumeHdmiClient = mHdmiTvClient;
+                    }
+
+                    if (fullVolumeHdmiClient != null
                             && mHdmiCecVolumeControlEnabled
                             && streamTypeAlias == AudioSystem.STREAM_MUSIC
                             // vol change on a full volume device
@@ -2982,6 +2980,10 @@
                                 keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
                                 break;
                             case AudioManager.ADJUST_TOGGLE_MUTE:
+                            case AudioManager.ADJUST_MUTE:
+                            case AudioManager.ADJUST_UNMUTE:
+                                // Many CEC devices only support toggle mute. Therefore, we send the
+                                // same keycode for all three mute options.
                                 keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
                                 break;
                             default:
@@ -2990,17 +2992,16 @@
                         if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
                             final long ident = Binder.clearCallingIdentity();
                             try {
-                                final long time = java.lang.System.currentTimeMillis();
                                 switch (keyEventMode) {
                                     case VOL_ADJUST_NORMAL:
-                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
-                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+                                        fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
+                                        fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
                                         break;
                                     case VOL_ADJUST_START:
-                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
+                                        fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
                                         break;
                                     case VOL_ADJUST_END:
-                                        mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+                                        fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
                                         break;
                                     default:
                                         Log.e(TAG, "Invalid keyEventMode " + keyEventMode);
@@ -3055,27 +3056,6 @@
         Binder.restoreCallingIdentity(identity);
     }
 
-    private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
-        // Sets the audio volume of AVR when we are in system audio mode. The new volume info
-        // is tranformed to HDMI-CEC commands and passed through CEC bus.
-        synchronized (mHdmiClientLock) {
-            if (mHdmiManager == null
-                    || mHdmiTvClient == null
-                    || oldVolume == newVolume
-                    || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
-                    || !mHdmiSystemAudioSupported
-                    || !mHdmiCecVolumeControlEnabled) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-
     // StreamVolumeCommand contains the information needed to defer the process of
     // setStreamVolume() in case the user has to acknowledge the safe volume warning message.
     static class StreamVolumeCommand {
@@ -3182,7 +3162,7 @@
 
     /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
     public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
-                                            String callingPackage) {
+            String callingPackage, String attributionTag) {
         enforceModifyAudioRoutingPermission();
         Objects.requireNonNull(attr, "attr must not be null");
         final int volumeGroup = getVolumeGroupIdForAttributes(attr);
@@ -3207,7 +3187,7 @@
                 continue;
             }
             setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
-                            Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
+                    attributionTag, Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
         }
     }
 
@@ -3249,9 +3229,15 @@
         return AudioSystem.getMinVolumeIndexForAttributes(attr);
     }
 
+    /** Retain API for unsupported app usage */
+    public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+        setStreamVolumeWithAttribution(streamType, index, flags, callingPackage, null);
+    }
+
     /** @see AudioManager#setStreamVolume(int, int, int)
      * Part of service interface, check permissions here */
-    public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+    public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+            String callingPackage, String attributionTag) {
         if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
             Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
                     + " CHANGE_ACCESSIBILITY_VOLUME  callingPackage=" + callingPackage);
@@ -3277,7 +3263,7 @@
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                 index/*val1*/, flags/*val2*/, callingPackage));
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
+                attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
     }
 
     private boolean canChangeAccessibilityVolume() {
@@ -3512,7 +3498,8 @@
     }
 
     private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
-            String caller, int uid, boolean hasModifyAudioSettings) {
+            String caller, String attributionTag, int uid,
+            boolean hasModifyAudioSettings) {
         if (DEBUG_VOL) {
             Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
                     + ", calling=" + callingPackage + ")");
@@ -3539,8 +3526,8 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -3579,10 +3566,6 @@
                 mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
             }
 
-            if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
-                setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags);
-            }
-
             flags &= ~AudioManager.FLAG_FIXED_VOLUME;
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
                 flags |= AudioManager.FLAG_FIXED_VOLUME;
@@ -3626,7 +3609,7 @@
         }
         // The default volume group is the one hosted by default product strategy, i.e.
         // supporting Default Attributes
-        return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
+        return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
     }
 
     private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
@@ -3844,18 +3827,6 @@
         }
     }
 
-    private void setSystemAudioMute(boolean state) {
-        synchronized (mHdmiClientLock) {
-            if (mHdmiManager == null || mHdmiTvClient == null || !mHdmiSystemAudioSupported) return;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHdmiTvClient.setSystemAudioMute(state);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-
     /** get stream mute state. */
     public boolean isStreamMute(int streamType) {
         if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -3968,15 +3939,15 @@
     }
 
     private void setMasterMuteInternal(boolean mute, int flags, String callingPackage, int uid,
-            int userId) {
+            int userId, String attributionTag) {
         // If we are being called by the system check for user we are going to change
         // so we handle user restrictions correctly.
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
         }
         // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
-        if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid,
+                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
@@ -4006,7 +3977,6 @@
         if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM)
                 || (getCurrentUserId() == userId)) {
             if (mute != AudioSystem.getMasterMute()) {
-                setSystemAudioMute(mute);
                 AudioSystem.setMasterMute(mute);
                 sendMasterMuteUpdate(mute, flags);
             }
@@ -4018,10 +3988,12 @@
         return AudioSystem.getMasterMute();
     }
 
-    public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) {
+    /** @see AudioManager#setMasterMute(boolean, int) */
+    public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+            String attributionTag) {
         enforceModifyAudioRoutingPermission();
-        setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(),
-                userId);
+        setMasterMuteInternal(mute, flags, callingPackage,
+                Binder.getCallingUid(), userId, attributionTag);
     }
 
     /** @see AudioManager#getStreamVolume(int) */
@@ -4092,7 +4064,8 @@
 
     /** @see AudioManager#setMicrophoneMute(boolean) */
     @Override
-    public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
+    public void setMicrophoneMute(boolean on, String callingPackage, int userId,
+            String attributionTag) {
         // If we are being called by the system check for user we are going to change
         // so we handle user restrictions correctly.
         int uid = Binder.getCallingUid();
@@ -4107,8 +4080,8 @@
                         ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE);
 
         // If OP_MUTE_MICROPHONE is set, disallow unmuting.
-        if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid,
+                callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record();
             return;
         }
@@ -4944,7 +4917,7 @@
         }
 
         adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
-                hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
+                null, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
     }
 
     /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4956,7 +4929,7 @@
             throw new SecurityException("Should only be called from system process");
         }
 
-        setStreamVolume(streamType, index, flags, packageName, packageName, uid,
+        setStreamVolume(streamType, index, flags, packageName, packageName, null, uid,
                 hasAudioSettingsPermission(uid, pid));
     }
 
@@ -6366,7 +6339,7 @@
 
     private void ensureValidAttributes(AudioVolumeGroup avg) {
         boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
-                .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+                .anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
         if (!hasAtLeastOneValidAudioAttributes) {
             throw new IllegalArgumentException("Volume Group " + avg.name()
                     + " has no valid audio attributes");
@@ -6414,7 +6387,7 @@
         private int mIndexMax;
         private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
         private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
-        private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+        private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
 
         // No API in AudioSystem to get a device from strategy or from attributes.
         // Need a valid public stream type to use current API getDeviceForStream
@@ -6427,8 +6400,9 @@
             if (DEBUG_VOL) {
                 Log.v(TAG, "VolumeGroupState for " + avg.toString());
             }
+            // mAudioAttributes is the default at this point
             for (final AudioAttributes aa : avg.getAudioAttributes()) {
-                if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                if (!aa.equals(mAudioAttributes)) {
                     mAudioAttributes = aa;
                     break;
                 }
@@ -8078,8 +8052,8 @@
     }
 
     public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
-            IAudioPolicyCallback pcb, int sdk) {
+            IAudioFocusDispatcher fd, String clientId, String callingPackageName,
+            String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
         final int uid = Binder.getCallingUid();
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
                 .setUid(uid)
@@ -8131,7 +8105,7 @@
         }
         mmi.record();
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, flags, sdk,
+                clientId, callingPackageName, attributionTag, flags, sdk,
                 forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/);
     }
 
@@ -8148,7 +8122,7 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, AudioManager.AUDIOFOCUS_FLAG_TEST,
+                clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
                 sdk, false /*forceDuck*/, fakeUid);
     }
 
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa..6fe1295 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -857,6 +857,7 @@
      * @param fd
      * @param clientId
      * @param callingPackageName
+     * @param attributionTag
      * @param flags
      * @param sdk
      * @param forceDuck only true if
@@ -868,7 +869,7 @@
      */
     protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
-            int flags, int sdk, boolean forceDuck, int testUid) {
+            String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) {
         new MediaMetrics.Item(mMetricsId)
                 .setUid(Binder.getCallingUid())
                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
@@ -903,7 +904,7 @@
         if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
                 // note we're using the real uid for appOp evaluation
                 && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
-                        callingPackageName) != AppOpsManager.MODE_ALLOWED)) {
+                        callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) {
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
 
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 183fabd..85817be 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
@@ -263,7 +263,7 @@
             final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
 
             // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
-            long identity = Binder.clearCallingIdentity();
+            final long identity1 = Binder.clearCallingIdentity();
             try {
                 if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
                     // If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -273,7 +273,7 @@
                     return;
                 }
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                Binder.restoreCallingIdentity(identity1);
             }
 
             final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
@@ -297,11 +297,11 @@
                     provider.second.getSensorProperties(sensorId);
             if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
                     && sensorProps != null && sensorProps.isAnyUdfpsType()) {
-                identity = Binder.clearCallingIdentity();
+                final long identity2 = Binder.clearCallingIdentity();
                 try {
                     authenticateWithPrompt(operationId, sensorProps, userId, receiver);
                 } finally {
-                    Binder.restoreCallingIdentity(identity);
+                    Binder.restoreCallingIdentity(identity2);
                 }
             } else {
                 provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index b2d35f4..ed7d185 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -35,11 +35,14 @@
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
 import android.hardware.camera2.CameraMetadata;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.hardware.display.DisplayManager;
 import android.media.AudioManager;
 import android.nfc.INfcAdapter;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
@@ -57,8 +60,8 @@
 import android.view.Surface;
 import android.view.WindowManagerGlobal;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -447,6 +450,8 @@
         }
     };
 
+    private final FoldStateListener mFoldStateListener;
+
     public CameraServiceProxy(Context context) {
         super(context);
         mContext = context;
@@ -459,6 +464,14 @@
         // Don't keep any extra logging threads if not needed
         mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS);
         mLogWriterService.allowCoreThreadTimeOut(true);
+
+        mFoldStateListener = new FoldStateListener(mContext, folded -> {
+            if (folded) {
+                setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+            } else {
+                clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+            }
+        });
     }
 
     /**
@@ -471,7 +484,7 @@
      *
      * @see #clearDeviceStateFlags(int)
      */
-    public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+    private void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
             mDeviceState |= deviceStateFlags;
@@ -491,7 +504,7 @@
      *
      * @see #setDeviceStateFlags(int)
      */
-    public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+    private void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
             mDeviceState &= ~deviceStateFlags;
@@ -555,6 +568,9 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to register display window listener!");
             }
+
+            mContext.getSystemService(DeviceStateManager.class)
+                    .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
         }
     }
 
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
new file mode 100644
index 0000000..a81213d
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A utility class for parsing App Compat Overrides flags.
+ *
+ * @hide
+ */
+final class AppCompatOverridesParser {
+    /**
+     * Flag for specifying all compat change IDs owned by a namespace. See {@link
+     * #parseOwnedChangeIds} for information on how this flag is parsed.
+     */
+    static final String FLAG_OWNED_CHANGE_IDS = "owned_change_ids";
+
+    /**
+     * Flag for immediately removing overrides for certain packages and change IDs (from the compat
+     * platform), as well as stopping to apply them, in case of an emergency. See {@link
+     * #parseRemoveOverrides} for information on how this flag is parsed.
+     */
+    static final String FLAG_REMOVE_OVERRIDES = "remove_overrides";
+
+    private static final String TAG = "AppCompatOverridesParser";
+
+    private static final String WILDCARD_SYMBOL = "*";
+
+    private static final Pattern BOOLEAN_PATTERN =
+            Pattern.compile("true|false", Pattern.CASE_INSENSITIVE);
+
+    private static final String WILDCARD_NO_OWNED_CHANGE_IDS_WARNING =
+            "Wildcard can't be used in '" + FLAG_REMOVE_OVERRIDES + "' flag with an empty "
+                    + FLAG_OWNED_CHANGE_IDS + "' flag";
+
+    private final PackageManager mPackageManager;
+
+    AppCompatOverridesParser(PackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    /**
+     * Parses the given {@code configStr} and returns a map from package name to a set of change
+     * IDs to remove for that package.
+     *
+     * <p>The given {@code configStr} is expected to either be:
+     *
+     * <ul>
+     *   <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+     *   ownedChangeIds}, for all installed packages should be removed.
+     *   <li>A comma separated key value list, where the key is a package name and the value is
+     *       either:
+     *       <ul>
+     *         <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+     *         ownedChangeIds} for that package should be removed.
+     *         <li>A colon separated list of change IDs to remove for that package.
+     *       </ul>
+     * </ul>
+     *
+     * <p>If the given {@code configStr} doesn't match the expected format, an empty map will be
+     * returned. If a specific change ID isn't a valid long, it will be ignored.
+     */
+    Map<String, Set<Long>> parseRemoveOverrides(String configStr, Set<Long> ownedChangeIds) {
+        if (configStr.isEmpty()) {
+            return emptyMap();
+        }
+
+        Map<String, Set<Long>> result = new ArrayMap<>();
+        if (configStr.equals(WILDCARD_SYMBOL)) {
+            if (ownedChangeIds.isEmpty()) {
+                Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+                return emptyMap();
+            }
+            List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+                    MATCH_ANY_USER);
+            for (ApplicationInfo appInfo : installedApps) {
+                result.put(appInfo.packageName, ownedChangeIds);
+            }
+            return result;
+        }
+
+        KeyValueListParser parser = new KeyValueListParser(',');
+        try {
+            parser.setString(configStr);
+        } catch (IllegalArgumentException e) {
+            Slog.w(
+                    TAG,
+                    "Invalid format in '" + FLAG_REMOVE_OVERRIDES + "' flag: " + configStr, e);
+            return emptyMap();
+        }
+        for (int i = 0; i < parser.size(); i++) {
+            String packageName = parser.keyAt(i);
+            String changeIdsStr = parser.getString(packageName, /* def= */ "");
+            if (changeIdsStr.equals(WILDCARD_SYMBOL)) {
+                if (ownedChangeIds.isEmpty()) {
+                    Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+                    continue;
+                }
+                result.put(packageName, ownedChangeIds);
+            } else {
+                for (String changeIdStr : changeIdsStr.split(":")) {
+                    try {
+                        long changeId = Long.parseLong(changeIdStr);
+                        result.computeIfAbsent(packageName, k -> new ArraySet<>()).add(changeId);
+                    } catch (NumberFormatException e) {
+                        Slog.w(
+                                TAG,
+                                "Invalid change ID in '" + FLAG_REMOVE_OVERRIDES + "' flag: "
+                                        + changeIdStr, e);
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Parses the given {@code configStr}, that is expected to be a comma separated list of change
+     * IDs, into a set.
+     *
+     * <p>If any of the change IDs isn't a valid long, it will be ignored.
+     */
+    static Set<Long> parseOwnedChangeIds(String configStr) {
+        if (configStr.isEmpty()) {
+            return emptySet();
+        }
+
+        Set<Long> result = new ArraySet<>();
+        for (String changeIdStr : configStr.split(",")) {
+            try {
+                result.add(Long.parseLong(changeIdStr));
+            } catch (NumberFormatException e) {
+                Slog.w(TAG,
+                        "Invalid change ID in '" + FLAG_OWNED_CHANGE_IDS + "' flag: " + changeIdStr,
+                        e);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Parses the given {@code configStr}, that is expected to be a comma separated list of changes
+     * overrides, and returns a {@link PackageOverrides}.
+     *
+     * <p>Each change override is in the following format:
+     * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty,
+     * this indicates that any override for the specified change ID should be removed.
+     *
+     * <p>If there are multiple overrides that should be added with the same change ID, the one
+     * that best fits the given {@code versionCode} is added.
+     *
+     * <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored.
+     *
+     * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the
+     * same change ID is both added and removed, i.e., has a change override entry with an empty
+     * enabled and another with a non-empty enabled, the change ID will only be removed.
+     */
+    static PackageOverrides parsePackageOverrides(
+            String configStr, long versionCode, Set<Long> changeIdsToSkip) {
+        if (configStr.isEmpty()) {
+            return new PackageOverrides();
+        }
+        PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
+        Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
+        Set<Long> overridesToRemove = new ArraySet<>();
+        for (String overrideEntryString : configStr.split(",")) {
+            List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
+            if (changeIdAndVersions.size() != 4) {
+                Slog.w(TAG, "Invalid change override entry: " + overrideEntryString);
+                continue;
+            }
+            long changeId;
+            try {
+                changeId = Long.parseLong(changeIdAndVersions.get(0));
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "Invalid change ID in override entry: " + overrideEntryString, e);
+                continue;
+            }
+
+            if (changeIdsToSkip.contains(changeId)) {
+                continue;
+            }
+
+            String minVersionCodeStr = changeIdAndVersions.get(1);
+            String maxVersionCodeStr = changeIdAndVersions.get(2);
+
+            String enabledStr = changeIdAndVersions.get(3);
+            if (enabledStr.isEmpty()) {
+                if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) {
+                    Slog.w(
+                            TAG,
+                            "min/max version code should be empty if enabled is empty: "
+                                    + overrideEntryString);
+                }
+                overridesToRemove.add(changeId);
+                continue;
+            }
+            if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) {
+                Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString);
+                continue;
+            }
+            boolean enabled = Boolean.parseBoolean(enabledStr);
+            PackageOverride.Builder overrideBuilder = new PackageOverride.Builder().setEnabled(
+                    enabled);
+            try {
+                if (!minVersionCodeStr.isEmpty()) {
+                    overrideBuilder.setMinVersionCode(Long.parseLong(minVersionCodeStr));
+                }
+                if (!maxVersionCodeStr.isEmpty()) {
+                    overrideBuilder.setMaxVersionCode(Long.parseLong(maxVersionCodeStr));
+                }
+            } catch (NumberFormatException e) {
+                Slog.w(TAG,
+                        "Invalid min/max version code in override entry: " + overrideEntryString,
+                        e);
+                continue;
+            }
+
+            try {
+                PackageOverride override = overrideBuilder.build();
+                if (!overridesToAdd.containsKey(changeId)
+                        || comparator.compare(override, overridesToAdd.get(changeId)) < 0) {
+                    overridesToAdd.put(changeId, override);
+                }
+            } catch (IllegalArgumentException e) {
+                Slog.w(TAG, "Failed to build PackageOverride", e);
+            }
+        }
+
+        for (Long changeId : overridesToRemove) {
+            if (overridesToAdd.containsKey(changeId)) {
+                Slog.w(
+                        TAG,
+                        "Change ID ["
+                                + changeId
+                                + "] is both added and removed in package override flag: "
+                                + configStr);
+                overridesToAdd.remove(changeId);
+            }
+        }
+
+        return new PackageOverrides(overridesToAdd, overridesToRemove);
+    }
+
+    /**
+     * A container for a map from change ID to {@link PackageOverride} to add and a set of change
+     * IDs to remove overrides for.
+     *
+     * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive.
+     */
+    static final class PackageOverrides {
+        public final Map<Long, PackageOverride> overridesToAdd;
+        public final Set<Long> overridesToRemove;
+
+        PackageOverrides() {
+            this(emptyMap(), emptySet());
+        }
+
+        PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) {
+            this.overridesToAdd = overridesToAdd;
+            this.overridesToRemove = overridesToRemove;
+        }
+    }
+
+    /**
+     * A {@link Comparator} that compares @link PackageOverride} instances with respect to a
+     * specified {@code versionCode} as follows:
+     *
+     * <ul>
+     *   <li>Prefer the {@link PackageOverride} whose version range contains {@code versionCode}.
+     *   <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+     *       versionCode} from below.
+     *   <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+     *       versionCode} from above.
+     * </ul>
+     */
+    private static final class PackageOverrideComparator implements Comparator<PackageOverride> {
+        private final long mVersionCode;
+
+        PackageOverrideComparator(long versionCode) {
+            this.mVersionCode = versionCode;
+        }
+
+        @Override
+        public int compare(PackageOverride o1, PackageOverride o2) {
+            // Prefer overrides whose version range contains versionCode.
+            boolean isVersionInRange1 = isVersionInRange(o1, mVersionCode);
+            boolean isVersionInRange2 = isVersionInRange(o2, mVersionCode);
+            if (isVersionInRange1 != isVersionInRange2) {
+                return isVersionInRange1 ? -1 : 1;
+            }
+
+            // Otherwise, prefer overrides whose version range is before versionCode.
+            boolean isVersionAfterRange1 = isVersionAfterRange(o1, mVersionCode);
+            boolean isVersionAfterRange2 = isVersionAfterRange(o2, mVersionCode);
+            if (isVersionAfterRange1 != isVersionAfterRange2) {
+                return isVersionAfterRange1 ? -1 : 1;
+            }
+
+            // If both overrides' version ranges are either before or after versionCode, prefer
+            // those whose version range is closer to versionCode.
+            return Long.compare(
+                    getVersionProximity(o1, mVersionCode), getVersionProximity(o2, mVersionCode));
+        }
+
+        /**
+         * Returns true if the version range in the given {@code override} contains {@code
+         * versionCode}.
+         */
+        private static boolean isVersionInRange(PackageOverride override, long versionCode) {
+            return override.getMinVersionCode() <= versionCode
+                    && versionCode <= override.getMaxVersionCode();
+        }
+
+        /**
+         * Returns true if the given {@code versionCode} is strictly after the version range in the
+         * given {@code override}.
+         */
+        private static boolean isVersionAfterRange(PackageOverride override, long versionCode) {
+            return override.getMaxVersionCode() < versionCode;
+        }
+
+        /**
+         * Returns true if the given {@code versionCode} is strictly before the version range in the
+         * given {@code override}.
+         */
+        private static boolean isVersionBeforeRange(PackageOverride override, long versionCode) {
+            return override.getMinVersionCode() > versionCode;
+        }
+
+        /**
+         * In case the given {@code versionCode} is strictly before or after the version range in
+         * the given {@code override}, returns the distance from it, otherwise returns zero.
+         */
+        private static long getVersionProximity(PackageOverride override, long versionCode) {
+            if (isVersionAfterRange(override, versionCode)) {
+                return versionCode - override.getMaxVersionCode();
+            }
+            if (isVersionBeforeRange(override, versionCode)) {
+                return override.getMinVersionCode() - versionCode;
+            }
+
+            // Version is in range. Note that when two overrides have a zero version proximity
+            // they will be ordered arbitrarily.
+            return 0;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
new file mode 100644
index 0000000..63ae1af
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.provider.DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES;
+
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+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.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.SystemService;
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service for applying per-app compat overrides delivered via Device Config.
+ *
+ * <p>The service listens both on changes to supported Device Config namespaces and on package
+ * added/changed/removed events, and applies overrides accordingly.
+ *
+ * @hide
+ */
+public final class AppCompatOverridesService {
+    private static final String TAG = "AppCompatOverridesService";
+
+    private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(
+            NAMESPACE_APP_COMPAT_OVERRIDES);
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPlatformCompat mPlatformCompat;
+    private final List<String> mSupportedNamespaces;
+    private final AppCompatOverridesParser mOverridesParser;
+    private final PackageReceiver mPackageReceiver;
+    private final List<DeviceConfigListener> mDeviceConfigListeners;
+
+    private AppCompatOverridesService(Context context) {
+        this(context, IPlatformCompat.Stub.asInterface(
+                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)), SUPPORTED_NAMESPACES);
+    }
+
+    @VisibleForTesting
+    AppCompatOverridesService(Context context, IPlatformCompat platformCompat,
+            List<String> supportedNamespaces) {
+        mContext = context;
+        mPackageManager = mContext.getPackageManager();
+        mPlatformCompat = platformCompat;
+        mSupportedNamespaces = supportedNamespaces;
+        mOverridesParser = new AppCompatOverridesParser(mPackageManager);
+        mPackageReceiver = new PackageReceiver(mContext);
+        mDeviceConfigListeners = new ArrayList<>();
+        for (String namespace : mSupportedNamespaces) {
+            mDeviceConfigListeners.add(new DeviceConfigListener(mContext, namespace));
+        }
+    }
+
+    @Override
+    public void finalize() {
+        unregisterDeviceConfigListeners();
+        unregisterPackageReceiver();
+    }
+
+    @VisibleForTesting
+    void registerDeviceConfigListeners() {
+        for (DeviceConfigListener listener : mDeviceConfigListeners) {
+            listener.register();
+        }
+    }
+
+    private void unregisterDeviceConfigListeners() {
+        for (DeviceConfigListener listener : mDeviceConfigListeners) {
+            listener.unregister();
+        }
+    }
+
+    @VisibleForTesting
+    void registerPackageReceiver() {
+        mPackageReceiver.register();
+    }
+
+    private void unregisterPackageReceiver() {
+        mPackageReceiver.unregister();
+    }
+
+    /**
+     * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code
+     * namespace} are fetched via {@link DeviceConfig#getProperties}.
+     */
+    private void applyAllOverrides(String namespace,
+            Map<String, Set<Long>> packageToChangeIdsToSkip) {
+        applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip);
+    }
+
+    /**
+     * Iterates all package override flags in the given {@code properties}, and for each flag whose
+     * package is installed on the device, parses its value and applies the overrides in it with
+     * respect to the package's current installed version.
+     */
+    private void applyOverrides(Properties properties,
+            Map<String, Set<Long>> packageToChangeIdsToSkip) {
+        Set<String> packageNames = new ArraySet<>(properties.getKeyset());
+        packageNames.remove(FLAG_OWNED_CHANGE_IDS);
+        packageNames.remove(FLAG_REMOVE_OVERRIDES);
+        for (String packageName : packageNames) {
+            Long versionCode = getVersionCodeOrNull(packageName);
+            if (versionCode == null) {
+                // Package isn't installed yet.
+                continue;
+            }
+
+            applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""),
+                    packageName, versionCode,
+                    packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()));
+        }
+    }
+
+    /**
+     * Applies all overrides in all supported namespaces for the given {@code packageName}.
+     */
+    private void applyAllPackageOverrides(String packageName) {
+        Long versionCode = getVersionCodeOrNull(packageName);
+        if (versionCode == null) {
+            return;
+        }
+
+        for (String namespace : mSupportedNamespaces) {
+            // We apply overrides for each namespace separately so that if there is a failure for
+            // one namespace, the other namespaces won't be affected.
+            applyPackageOverrides(
+                    DeviceConfig.getString(namespace, packageName, /* defaultValue= */ ""),
+                    packageName, versionCode,
+                    getOverridesToRemove(namespace).getOrDefault(packageName, emptySet()));
+        }
+    }
+
+    /**
+     * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the
+     * resulting {@link PackageOverrides#overridesToAdd} via {@link
+     * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link
+     * PackageOverrides#overridesToRemove} via {@link
+     * IPlatformCompat#removeOverridesOnReleaseBuilds}.
+     */
+    private void applyPackageOverrides(String configStr, String packageName,
+            long versionCode, Set<Long> changeIdsToSkip) {
+        PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides(
+                configStr, versionCode, changeIdsToSkip);
+        putPackageOverrides(packageName, packageOverrides.overridesToAdd);
+        removePackageOverrides(packageName, packageOverrides.overridesToRemove);
+    }
+
+    /**
+     * Removes all owned overrides in all supported namespaces for the given {@code packageName}.
+     *
+     * <p>If a certain namespace doesn't have a package override flag for the given {@code
+     * packageName}, that namespace is skipped.</p>
+     */
+    private void removeAllPackageOverrides(String packageName) {
+        for (String namespace : mSupportedNamespaces) {
+            if (DeviceConfig.getString(namespace, packageName, /* defaultValue= */ "").isEmpty()) {
+                // No overrides for this package in this namespace.
+                continue;
+            }
+            // We remove overrides for each namespace separately so that if there is a failure for
+            // one namespace, the other namespaces won't be affected.
+            removePackageOverrides(packageName, getOwnedChangeIds(namespace));
+        }
+    }
+
+    /**
+     * Calls {@link IPlatformCompat#removeOverridesOnReleaseBuilds} on each package name and
+     * respective change IDs in {@code overridesToRemove}.
+     */
+    private void removeOverrides(Map<String, Set<Long>> overridesToRemove) {
+        for (Map.Entry<String, Set<Long>> packageNameAndOverrides : overridesToRemove.entrySet()) {
+            removePackageOverrides(packageNameAndOverrides.getKey(),
+                    packageNameAndOverrides.getValue());
+        }
+    }
+
+    /**
+     * Fetches the value of {@link AppCompatOverridesParser#FLAG_REMOVE_OVERRIDES} for the given
+     * {@code namespace} and parses it into a map from package name to a set of change IDs to
+     * remove for that package.
+     */
+    private Map<String, Set<Long>> getOverridesToRemove(String namespace) {
+        return mOverridesParser.parseRemoveOverrides(
+                DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""),
+                getOwnedChangeIds(namespace));
+    }
+
+    /**
+     * Fetches the value of {@link AppCompatOverridesParser#FLAG_OWNED_CHANGE_IDS} for the given
+     * {@code namespace} and parses it into a set of change IDs.
+     */
+    private static Set<Long> getOwnedChangeIds(String namespace) {
+        return AppCompatOverridesParser.parseOwnedChangeIds(
+                DeviceConfig.getString(namespace, FLAG_OWNED_CHANGE_IDS, /* defaultValue= */ ""));
+    }
+
+    private void putPackageOverrides(String packageName,
+            Map<Long, PackageOverride> overridesToAdd) {
+        if (overridesToAdd.isEmpty()) {
+            return;
+        }
+        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overridesToAdd);
+        try {
+            mPlatformCompat.putOverridesOnReleaseBuilds(config, packageName);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
+        }
+    }
+
+    private void removePackageOverrides(String packageName, Set<Long> overridesToRemove) {
+        if (overridesToRemove.isEmpty()) {
+            return;
+        }
+        CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+                overridesToRemove);
+        try {
+            mPlatformCompat.removeOverridesOnReleaseBuilds(config, packageName);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to call IPlatformCompat#removeOverridesOnReleaseBuilds", e);
+        }
+    }
+
+    private boolean isInstalledForAnyUser(String packageName) {
+        return getVersionCodeOrNull(packageName) != null;
+    }
+
+    @Nullable
+    private Long getVersionCodeOrNull(String packageName) {
+        try {
+            ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName,
+                    MATCH_ANY_USER);
+            return applicationInfo.longVersionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            // Package isn't installed for any user.
+            return null;
+        }
+    }
+
+    /**
+     * SystemService lifecycle for AppCompatOverridesService.
+     *
+     * @hide
+     */
+    public static final class Lifecycle extends SystemService {
+        private AppCompatOverridesService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new AppCompatOverridesService(getContext());
+            mService.registerDeviceConfigListeners();
+            mService.registerPackageReceiver();
+        }
+    }
+
+    /**
+     * A {@link DeviceConfig.OnPropertiesChangedListener} that listens on changes to a given
+     * namespace and adds/removes overrides according to the changed flags.
+     */
+    private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+        private final Context mContext;
+        private final String mNamespace;
+
+        private DeviceConfigListener(Context context, String namespace) {
+            mContext = context;
+            mNamespace = namespace;
+        }
+
+        private void register() {
+            DeviceConfig.addOnPropertiesChangedListener(mNamespace, mContext.getMainExecutor(),
+                    this);
+        }
+
+        private void unregister() {
+            DeviceConfig.removeOnPropertiesChangedListener(this);
+        }
+
+        @Override
+        public void onPropertiesChanged(Properties properties) {
+            boolean removeOverridesFlagChanged = properties.getKeyset().contains(
+                    FLAG_REMOVE_OVERRIDES);
+            boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains(
+                    FLAG_OWNED_CHANGE_IDS);
+
+            Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace);
+            if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) {
+                // In both cases it's possible that overrides that weren't removed before should
+                // now be removed.
+                removeOverrides(overridesToRemove);
+            }
+
+            if (removeOverridesFlagChanged) {
+                // We need to re-apply all overrides in the namespace since the remove overrides
+                // flag might have blocked some of them from being applied before.
+                applyAllOverrides(mNamespace, overridesToRemove);
+            } else {
+                applyOverrides(properties, overridesToRemove);
+            }
+        }
+    }
+
+    /**
+     * A {@link BroadcastReceiver} that listens on package added/changed/removed events and
+     * adds/removes overrides according to the corresponding Device Config flags.
+     */
+    private final class PackageReceiver extends BroadcastReceiver {
+        private final Context mContext;
+        private final IntentFilter mIntentFilter;
+
+        private PackageReceiver(Context context) {
+            mContext = context;
+            mIntentFilter = new IntentFilter();
+            mIntentFilter.addAction(ACTION_PACKAGE_ADDED);
+            mIntentFilter.addAction(ACTION_PACKAGE_CHANGED);
+            mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
+            mIntentFilter.addDataScheme("package");
+        }
+
+        private void register() {
+            mContext.registerReceiverForAllUsers(this, mIntentFilter, /* broadcastPermission= */
+                    null, /* scheduler= */ null);
+        }
+
+        private void unregister() {
+            mContext.unregisterReceiver(this);
+        }
+
+        @Override
+        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+            Uri data = intent.getData();
+            if (data == null) {
+                Slog.w(TAG, "Failed to get package name in package receiver");
+                return;
+            }
+            String packageName = data.getSchemeSpecificPart();
+            String action = intent.getAction();
+            if (action == null) {
+                Slog.w(TAG, "Failed to get action in package receiver");
+                return;
+            }
+            switch (action) {
+                case ACTION_PACKAGE_ADDED:
+                case ACTION_PACKAGE_CHANGED:
+                    applyAllPackageOverrides(packageName);
+                    break;
+                case ACTION_PACKAGE_REMOVED:
+                    if (!isInstalledForAnyUser(packageName)) {
+                        removeAllPackageOverrides(packageName);
+                    }
+                    break;
+                default:
+                    Slog.w(TAG, "Unsupported action in package receiver: " + action);
+                    break;
+            }
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 0000000..b80f340
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1,2 @@
+tomnatan@google.com
+mariiasand@google.com
diff --git a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
new file mode 100644
index 0000000..4b8f08e
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksMockingServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.compat.overrides"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1acbde9..565c9ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,8 +24,7 @@
 import static android.os.PowerWhitelistManager.REASON_VPN;
 import static android.os.UserHandle.PER_USER_RANGE;
 
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -1098,13 +1097,14 @@
             return Process.myUid();
         }
         PackageManager pm = mContext.getPackageManager();
-        return Binder.withCleanCallingIdentity(() -> {
-            try {
-                return pm.getPackageUidAsUser(app, userId);
-            } catch (NameNotFoundException e) {
-                return -1;
-            }
-        });
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return pm.getPackageUidAsUser(app, userId);
+        } catch (NameNotFoundException e) {
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private boolean doesPackageTargetAtLeastQ(String packageName) {
@@ -1280,15 +1280,16 @@
                 // We are user controlled, not driven by NetworkRequest.
             }
         };
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                mNetworkAgent.register();
-            } catch (final Exception e) {
-                // If register() throws, don't keep an unregistered agent.
-                mNetworkAgent = null;
-                throw e;
-            }
-        });
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mNetworkAgent.register();
+        } catch (final Exception e) {
+            // If register() throws, don't keep an unregistered agent.
+            mNetworkAgent = null;
+            throw e;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                 ? Arrays.asList(mConfig.underlyingNetworks) : null);
         updateState(DetailedState.CONNECTED, "agentConnect");
@@ -2026,13 +2027,16 @@
     }
 
     private void enforceNotRestrictedUser() {
-        Binder.withCleanCallingIdentity(() -> {
+        final long token = Binder.clearCallingIdentity();
+        try {
             final UserInfo user = mUserManager.getUserInfo(mUserId);
 
             if (user.isRestricted()) {
                 throw new SecurityException("Restricted users cannot configure VPNs");
             }
-        });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -2825,8 +2829,10 @@
 
         LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
             super(TAG);
-            checkArgument(racoon != null || mtpd != null, "Arguments to racoon and mtpd "
-                    + "must not both be null");
+            if (racoon == null && mtpd == null) {
+                throw new IllegalArgumentException(
+                        "Arguments to racoon and mtpd must not both be null");
+            }
             mConfig = config;
             mDaemons = new String[] {"racoon", "mtpd"};
             // TODO: clear arguments from memory once launched
@@ -3151,8 +3157,8 @@
      */
     public synchronized boolean provisionVpnProfile(
             @NonNull String packageName, @NonNull VpnProfile profile) {
-        checkNotNull(packageName, "No package name provided");
-        checkNotNull(profile, "No profile provided");
+        requireNonNull(packageName, "No package name provided");
+        requireNonNull(profile, "No profile provided");
 
         verifyCallingUidAndPackage(packageName);
         enforceNotRestrictedUser();
@@ -3169,12 +3175,12 @@
         }
 
         // Permissions checked during startVpnProfile()
-        Binder.withCleanCallingIdentity(
-                () -> {
-                    getVpnProfileStore().put(
-                            getProfileNameForPackage(packageName),
-                            encodedProfile);
-                });
+        final long token = Binder.clearCallingIdentity();
+        try {
+            getVpnProfileStore().put(getProfileNameForPackage(packageName), encodedProfile);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
 
         // TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
         // This mirrors the prepareAndAuthorize that is used by VpnService.
@@ -3194,26 +3200,28 @@
      */
     public synchronized void deleteVpnProfile(
             @NonNull String packageName) {
-        checkNotNull(packageName, "No package name provided");
+        requireNonNull(packageName, "No package name provided");
 
         verifyCallingUidAndPackage(packageName);
         enforceNotRestrictedUser();
 
-        Binder.withCleanCallingIdentity(
-                () -> {
-                    // If this profile is providing the current VPN, turn it off, disabling
-                    // always-on as well if enabled.
-                    if (isCurrentIkev2VpnLocked(packageName)) {
-                        if (mAlwaysOn) {
-                            // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
-                            setAlwaysOnPackage(null, false, null);
-                        } else {
-                            prepareInternal(VpnConfig.LEGACY_VPN);
-                        }
-                    }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // If this profile is providing the current VPN, turn it off, disabling
+            // always-on as well if enabled.
+            if (isCurrentIkev2VpnLocked(packageName)) {
+                if (mAlwaysOn) {
+                    // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+                    setAlwaysOnPackage(null, false, null);
+                } else {
+                    prepareInternal(VpnConfig.LEGACY_VPN);
+                }
+            }
 
-                    getVpnProfileStore().remove(getProfileNameForPackage(packageName));
-                });
+            getVpnProfileStore().remove(getProfileNameForPackage(packageName));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -3247,7 +3255,7 @@
      */
     public synchronized void startVpnProfile(
             @NonNull String packageName) {
-        checkNotNull(packageName, "No package name provided");
+        requireNonNull(packageName, "No package name provided");
 
         enforceNotRestrictedUser();
 
@@ -3256,15 +3264,17 @@
             throw new SecurityException("User consent not granted for package " + packageName);
         }
 
-        Binder.withCleanCallingIdentity(
-                () -> {
-                    final VpnProfile profile = getVpnProfilePrivileged(packageName);
-                    if (profile == null) {
-                        throw new IllegalArgumentException("No profile found for " + packageName);
-                    }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final VpnProfile profile = getVpnProfilePrivileged(packageName);
+            if (profile == null) {
+                throw new IllegalArgumentException("No profile found for " + packageName);
+            }
 
-                    startVpnProfilePrivileged(profile, packageName);
-                });
+            startVpnProfilePrivileged(profile, packageName);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private synchronized void startVpnProfilePrivileged(
@@ -3325,7 +3335,7 @@
      * @param packageName the package name of the app provisioning this profile
      */
     public synchronized void stopVpnProfile(@NonNull String packageName) {
-        checkNotNull(packageName, "No package name provided");
+        requireNonNull(packageName, "No package name provided");
 
         enforceNotRestrictedUser();
 
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c3c42ba..fadcce9 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -823,6 +824,21 @@
     }
 
     @Override
+    public String getSyncAdapterPackageAsUser(@NonNull String accountType,
+            @NonNull String authority, @UserIdInt int userId) {
+        enforceCrossUserPermission(userId,
+                "no permission to read sync settings for user " + userId);
+        final int callingUid = Binder.getCallingUid();
+        final long identityToken = clearCallingIdentity();
+        try {
+            return getSyncManager().getSyncAdapterPackageAsUser(accountType, authority,
+                    callingUid, userId);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    @Override
     public boolean getSyncAutomatically(Account account, String providerName) {
         return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
     }
@@ -1205,7 +1221,7 @@
             SyncManager syncManager = getSyncManager();
             if (syncManager != null && callback != null) {
                 syncManager.getSyncStorageEngine().addStatusChangeListener(
-                        mask, UserHandle.getUserId(callingUid), callback);
+                        mask, callingUid, callback);
             }
         } finally {
             restoreCallingIdentity(identityToken);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 53c13c7..cfd0a2d 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1245,6 +1245,26 @@
         return filteredResult.toArray(new String[] {});
     }
 
+    public String getSyncAdapterPackageAsUser(String accountType, String authority,
+            int callingUid, int userId) {
+        if (accountType == null || authority == null) {
+            return null;
+        }
+        final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+                mSyncAdapters.getServiceInfo(
+                        SyncAdapterType.newKey(authority, accountType),
+                        userId);
+        if (syncAdapterInfo == null) {
+            return null;
+        }
+        final String packageName = syncAdapterInfo.type.getPackageName();
+        if (TextUtils.isEmpty(packageName) || mPackageManagerInternal.filterAppAccess(
+                packageName, callingUid, userId)) {
+            return null;
+        }
+        return packageName;
+    }
+
     private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
             SyncResult syncResult) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
@@ -3402,7 +3422,7 @@
 
             scheduleSyncOperationH(op);
             mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
-                    target.userId);
+                    op.owningPackage, target.userId);
         }
 
         /**
@@ -3921,7 +3941,7 @@
                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
                     resultMessage, downstreamActivity, upstreamActivity,
-                    syncOperation.target.userId);
+                    syncOperation.owningPackage, syncOperation.target.userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 668142b..1894c0f 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -33,6 +33,7 @@
 import android.content.SyncRequest;
 import android.content.SyncStatusInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -59,6 +60,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IntPair;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -166,6 +168,8 @@
     private static HashMap<String, String> sAuthorityRenames;
     private static PeriodicSyncAddedListener mPeriodicSyncAddedListener;
 
+    private final PackageManagerInternal mPackageManagerInternal;
+
     private volatile boolean mIsClockValid;
 
     static {
@@ -525,6 +529,8 @@
         mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
 
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
         File systemDir = new File(dataDir, "system");
         mSyncDir = new File(systemDir, SYNC_DIR_NAME);
         mSyncDir.mkdirs();
@@ -609,9 +615,9 @@
         return mSyncRandomOffset;
     }
 
-    public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
+    public void addStatusChangeListener(int mask, int callingUid, ISyncStatusObserver callback) {
         synchronized (mAuthorities) {
-            final long cookie = IntPair.of(userId, mask);
+            final long cookie = IntPair.of(callingUid, mask);
             mChangeListeners.register(callback, cookie);
         }
     }
@@ -644,16 +650,32 @@
         }
     }
 
-    void reportChange(int which, int callingUserId) {
+    void reportChange(int which, EndPoint target) {
+        final String syncAdapterPackageName;
+        if (target.account == null || target.provider == null) {
+            syncAdapterPackageName = null;
+        } else {
+            syncAdapterPackageName = ContentResolver.getSyncAdapterPackageAsUser(
+                    target.account.type, target.provider, target.userId);
+        }
+        reportChange(which, syncAdapterPackageName, target.userId);
+    }
+
+    void reportChange(int which, String callingPackageName, int callingUserId) {
         ArrayList<ISyncStatusObserver> reports = null;
         synchronized (mAuthorities) {
             int i = mChangeListeners.beginBroadcast();
             while (i > 0) {
                 i--;
                 final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
-                final int userId = IntPair.first(cookie);
+                final int registerUid = IntPair.first(cookie);
+                final int registerUserId = UserHandle.getUserId(registerUid);
                 final int mask = IntPair.second(cookie);
-                if ((which & mask) == 0 || callingUserId != userId) {
+                if ((which & mask) == 0 || callingUserId != registerUserId) {
+                    continue;
+                }
+                if (callingPackageName != null && mPackageManagerInternal.filterAppAccess(
+                        callingPackageName, registerUid, callingUserId)) {
                     continue;
                 }
                 if (reports == null) {
@@ -716,12 +738,12 @@
                 " cuid=", callingUid,
                 " cpid=", callingPid
         );
+        final AuthorityInfo authority;
         synchronized (mAuthorities) {
-            AuthorityInfo authority =
-                    getOrCreateAuthorityLocked(
-                            new EndPoint(account, providerName, userId),
-                            -1 /* ident */,
-                            false);
+            authority = getOrCreateAuthorityLocked(
+                    new EndPoint(account, providerName, userId),
+                    -1 /* ident */,
+                    false);
             if (authority.enabled == sync) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Slog.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
@@ -743,7 +765,7 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, authority.target);
         queueBackup();
     }
 
@@ -811,7 +833,7 @@
             requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
                     ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
     }
 
     public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -857,7 +879,7 @@
             }
         }
         if (changed) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
         }
     }
 
@@ -919,7 +941,8 @@
         }
 
         for (int i = changedUserIds.size() - 1; i > 0; i--) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                    null /* callingPackageName */, changedUserIds.valueAt(i));
         }
     }
 
@@ -945,7 +968,7 @@
             }
             authority.delayUntil = delayUntil;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
     }
 
     /**
@@ -988,7 +1011,8 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                null /* callingPackageName */, userId);
         mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
         queueBackup();
     }
@@ -1039,7 +1063,7 @@
             SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
             status.pending = pendingValue;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info);
     }
 
     /**
@@ -1129,7 +1153,7 @@
                     activeSyncContext.mStartTime);
             getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
         }
-        reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
+        reportActiveChange(activeSyncContext.mSyncOperation.target);
         return syncInfo;
     }
 
@@ -1146,14 +1170,14 @@
             getCurrentSyncs(userId).remove(syncInfo);
         }
 
-        reportActiveChange(userId);
+        reportActiveChange(new EndPoint(syncInfo.account, syncInfo.authority, userId));
     }
 
     /**
      * To allow others to send active change reports, to poke clients.
      */
-    public void reportActiveChange(int userId) {
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
+    public void reportActiveChange(EndPoint target) {
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, target);
     }
 
     /**
@@ -1188,12 +1212,13 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.owningPackage, op.target.userId);
         return id;
     }
 
     public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
-                              long downstreamActivity, long upstreamActivity, int userId) {
+                              long downstreamActivity, long upstreamActivity, String opPackageName,
+                              int userId) {
         synchronized (mAuthorities) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1333,7 +1358,7 @@
             }
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, opPackageName, userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 35f2957..9f806af 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -43,6 +43,7 @@
     // The display device does not manage these properties itself, they are set by
     // the display manager service.  The display device shouldn't really be looking at these.
     private int mCurrentLayerStack = -1;
+    private int mCurrentFlags = 0;
     private int mCurrentOrientation = -1;
     private Rect mCurrentLayerStackRect;
     private Rect mCurrentDisplayRect;
@@ -212,6 +213,19 @@
     }
 
     /**
+     * Sets the display flags while in a transaction.
+     *
+     * Valid display flags:
+     *  {@link SurfaceControl#DISPLAY_RECEIVES_INPUT}
+     */
+    public final void setDisplayFlagsLocked(SurfaceControl.Transaction t, int flags) {
+        if (mCurrentFlags != flags) {
+            mCurrentFlags = flags;
+            t.setDisplayFlags(mDisplayToken, flags);
+        }
+    }
+
+    /**
      * Sets the display projection while in a transaction.
      *
      * @param orientation defines the display's orientation
@@ -298,6 +312,7 @@
         pw.println("mUniqueId=" + mUniqueId);
         pw.println("mDisplayToken=" + mDisplayToken);
         pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+        pw.println("mCurrentFlags=" + mCurrentFlags);
         pw.println("mCurrentOrientation=" + mCurrentOrientation);
         pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
         pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index f23ae6e..b0ad118 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -2057,7 +2057,9 @@
         public void observe() {
             StatusBarManagerInternal statusBar =
                     LocalServices.getService(StatusBarManagerInternal.class);
-            statusBar.setUdfpsHbmListener(this);
+            if (statusBar != null) {
+                statusBar.setUdfpsHbmListener(this);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 9acb4c8..5186744 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -512,6 +514,11 @@
             boolean isBlanked) {
         // Set the layer stack.
         device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
+        // Also inform whether the device is the same one sent to inputflinger for its layerstack.
+        // TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
+        device.setDisplayFlagsLocked(t,
+                device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE
+                        ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0);
 
         // Set the color mode and allowed display mode.
         if (device == mPrimaryDisplayDevice) {
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index bccd4c4..f9a1368 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -162,7 +162,8 @@
     private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
             new ReduceBrightColorsTintController();
 
-    private final Handler mHandler;
+    @VisibleForTesting
+    final Handler mHandler;
 
     private final AppSaturationController mAppSaturationController = new AppSaturationController();
 
@@ -404,13 +405,13 @@
         // existing activated state. This ensures consistency of tint across the color mode change.
         onDisplayColorModeChanged(getColorModeInternal());
 
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         if (mNightDisplayTintController.isAvailable(getContext())) {
             // Reset the activated state.
             mNightDisplayTintController.setActivated(null);
 
             // Prepare the night display color transformation matrix.
-            mNightDisplayTintController
-                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+            mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
             mNightDisplayTintController
                     .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
 
@@ -432,8 +433,7 @@
         }
 
         if (mReduceBrightColorsTintController.isAvailable(getContext())) {
-            mReduceBrightColorsTintController
-                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+            mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
             onReduceBrightColorsStrengthLevelChanged();
             final boolean reset = resetReduceBrightColors();
             if (!reset) {
@@ -540,8 +540,8 @@
         mDisplayWhiteBalanceTintController.cancelAnimator();
 
         if (mNightDisplayTintController.isAvailable(getContext())) {
-            mNightDisplayTintController
-                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+            final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+            mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
             mNightDisplayTintController
                     .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
         }
@@ -736,10 +736,11 @@
     @VisibleForTesting
     void updateDisplayWhiteBalanceStatus() {
         boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
                 && !mNightDisplayTintController.isActivated()
                 && !isAccessibilityEnabled()
-                && DisplayTransformManager.needsLinearColorMatrix());
+                && dtm.needsLinearColorMatrix());
         boolean activated = mDisplayWhiteBalanceTintController.isActivated();
 
         if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 5c68c51..0dba9e1 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -239,7 +239,7 @@
     /**
      * Return true when the color matrix works in linear space.
      */
-    public static boolean needsLinearColorMatrix() {
+    public boolean needsLinearColorMatrix() {
         return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
                 DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
     }
@@ -247,7 +247,7 @@
     /**
      * Return true when the specified colorMode requires the color matrix to work in linear space.
      */
-    public static boolean needsLinearColorMatrix(int colorMode) {
+    public boolean needsLinearColorMatrix(int colorMode) {
         return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 8405bbe..d422d51 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -88,7 +88,7 @@
                 tv.updateActiveSource(current, "ActiveSourceHandler");
                 invokeCallback(HdmiControlManager.RESULT_SUCCESS);
             } else {
-                tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
+                tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress,
                         mCallback);
             }
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index fefe953..5de89c9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -317,12 +317,25 @@
                 R.bool.config_cecHdmiCecVersion20_allowed,
                 R.bool.config_cecHdmiCecVersion20_default);
 
+        Setting routingControlControl = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+                R.bool.config_cecRoutingControl_userConfigurable);
+        routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_ENABLED,
+                R.bool.config_cecRoutingControlEnabled_allowed,
+                R.bool.config_cecRoutingControlEnabled_default);
+        routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_DISABLED,
+                R.bool.config_cecRoutingControlDisabled_allowed,
+                R.bool.config_cecRoutingControlDisabled_default);
+
         Setting powerControlMode = registerSetting(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                 R.bool.config_cecPowerControlMode_userConfigurable);
         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
                 R.bool.config_cecPowerControlModeTv_allowed,
                 R.bool.config_cecPowerControlModeTv_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
                 R.bool.config_cecPowerControlModeBroadcast_allowed,
                 R.bool.config_cecPowerControlModeBroadcast_default);
@@ -342,6 +355,16 @@
                 R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
                 R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
 
+        Setting systemAudioControl = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+                R.bool.config_cecSystemAudioControl_userConfigurable);
+        systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED,
+                R.bool.config_cecSystemAudioControlEnabled_allowed,
+                R.bool.config_cecSystemAudioControlEnabled_default);
+        systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_DISABLED,
+                R.bool.config_cecSystemAudioControlDisabled_allowed,
+                R.bool.config_cecSystemAudioControlDisabled_default);
+
         Setting systemAudioModeMuting = registerSetting(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 R.bool.config_cecSystemAudioModeMuting_userConfigurable);
@@ -498,12 +521,16 @@
                 return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
                 return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+                return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
                 return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
                 return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
                 return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+                return STORAGE_GLOBAL_SETTINGS;
             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
                 return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
@@ -535,12 +562,16 @@
                 return Global.HDMI_CONTROL_ENABLED;
             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
                 return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+                return Global.HDMI_CEC_SWITCH_ENABLED;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
                 return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
             case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
                 return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED;
             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
                 return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+                return Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED;
             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
                 return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 23b5c14..698ee0b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -481,4 +481,113 @@
         // return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
         return CEC_KEYCODE_MUTE;
     }
+
+    public static String getKeycodeType(byte keycode) {
+        switch (keycode) {
+            case CEC_KEYCODE_UP:
+            case CEC_KEYCODE_DOWN:
+            case CEC_KEYCODE_LEFT:
+            case CEC_KEYCODE_RIGHT:
+            case CEC_KEYCODE_RIGHT_UP:
+            case CEC_KEYCODE_RIGHT_DOWN:
+            case CEC_KEYCODE_LEFT_UP:
+            case CEC_KEYCODE_LEFT_DOWN:
+            case CEC_KEYCODE_PAGE_UP:
+            case CEC_KEYCODE_PAGE_DOWN:
+            case CEC_KEYCODE_EXIT:
+                return "Navigation";
+            case CEC_KEYCODE_ROOT_MENU:
+            case CEC_KEYCODE_SETUP_MENU:
+            case CEC_KEYCODE_CONTENTS_MENU:
+            case CEC_KEYCODE_FAVORITE_MENU:
+            case CEC_KEYCODE_MEDIA_TOP_MENU:
+            case CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU:
+                return "Menu";
+            case CEC_KEYCODE_VOLUME_UP:
+                return "Volume up";
+            case CEC_KEYCODE_VOLUME_DOWN:
+                return "Volume down";
+            case CEC_KEYCODE_MUTE:
+                return "Volume mute";
+            case CEC_KEYCODE_POWER:
+                return "Power";
+            case CEC_KEYCODE_POWER_TOGGLE_FUNCTION:
+                return "Power toggle";
+            case CEC_KEYCODE_POWER_OFF_FUNCTION:
+                return "Power off";
+            case CEC_KEYCODE_POWER_ON_FUNCTION:
+                return "Power on";
+            case CEC_KEYCODE_F1_BLUE:
+            case CEC_KEYCODE_F2_RED:
+            case CEC_KEYCODE_F3_GREEN:
+            case CEC_KEYCODE_F4_YELLOW:
+            case CEC_KEYCODE_F5:
+                return "Function key";
+            case CEC_KEYCODE_NEXT_FAVORITE:
+            case CEC_KEYCODE_CHANNEL_UP:
+            case CEC_KEYCODE_CHANNEL_DOWN:
+            case CEC_KEYCODE_PREVIOUS_CHANNEL:
+                return "Channel";
+            case CEC_KEYCODE_NUMBER_11:
+            case CEC_KEYCODE_NUMBER_12:
+            case CEC_KEYCODE_NUMBER_0_OR_NUMBER_10:
+            case CEC_KEYCODE_NUMBERS_1:
+            case CEC_KEYCODE_NUMBERS_2:
+            case CEC_KEYCODE_NUMBERS_3:
+            case CEC_KEYCODE_NUMBERS_4:
+            case CEC_KEYCODE_NUMBERS_5:
+            case CEC_KEYCODE_NUMBERS_6:
+            case CEC_KEYCODE_NUMBERS_7:
+            case CEC_KEYCODE_NUMBERS_8:
+            case CEC_KEYCODE_NUMBERS_9:
+                return "Number";
+            case CEC_KEYCODE_PLAY:
+            case CEC_KEYCODE_STOP:
+            case CEC_KEYCODE_PAUSE:
+            case CEC_KEYCODE_RECORD:
+            case CEC_KEYCODE_REWIND:
+            case CEC_KEYCODE_FAST_FORWARD:
+            case CEC_KEYCODE_EJECT:
+            case CEC_KEYCODE_FORWARD:
+            case CEC_KEYCODE_BACKWARD:
+            case CEC_KEYCODE_STOP_RECORD:
+            case CEC_KEYCODE_PAUSE_RECORD:
+            case CEC_KEYCODE_ANGLE:
+            case CEC_KEYCODE_SUB_PICTURE:
+            case CEC_KEYCODE_VIDEO_ON_DEMAND:
+                return "Media";
+            case CEC_KEYCODE_PLAY_FUNCTION:
+            case CEC_KEYCODE_PAUSE_PLAY_FUNCTION:
+            case CEC_KEYCODE_RECORD_FUNCTION:
+            case CEC_KEYCODE_PAUSE_RECORD_FUNCTION:
+            case CEC_KEYCODE_STOP_FUNCTION:
+            case CEC_KEYCODE_MUTE_FUNCTION:
+            case CEC_KEYCODE_RESTORE_VOLUME_FUNCTION:
+            case CEC_KEYCODE_TUNE_FUNCTION:
+            case CEC_KEYCODE_SELECT_MEDIA_FUNCTION:
+            case CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION:
+            case CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION:
+                return "Functional";
+            case CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE:
+            case CEC_KEYCODE_TIMER_PROGRAMMING:
+                return "Timer";
+            case CEC_KEYCODE_SOUND_SELECT:
+            case CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+            case CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+            case CEC_KEYCODE_INPUT_SELECT:
+            case CEC_KEYCODE_SELECT:
+                return "Select";
+            case CEC_KEYCODE_NUMBER_ENTRY_MODE:
+            case CEC_KEYCODE_DOT:
+            case CEC_KEYCODE_CLEAR:
+            case CEC_KEYCODE_ENTER:
+            case CEC_KEYCODE_DISPLAY_INFORMATION:
+            case CEC_KEYCODE_HELP:
+            case CEC_KEYCODE_DATA:
+            case CEC_KEYCODE_INITIAL_CONFIGURATION:
+                return "General";
+            default:
+                return "Unknown";
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a2cb78d..de65d76 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,11 +17,11 @@
 package com.android.server.hdmi;
 
 import android.annotation.CallSuper;
-import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.input.InputManager;
+import android.hardware.tv.cec.V1_0.Result;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.media.AudioManager;
 import android.os.Handler;
@@ -281,11 +281,11 @@
             case Constants.MESSAGE_SET_MENU_LANGUAGE:
                 return handleSetMenuLanguage(message);
             case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
-                return handleGivePhysicalAddress(null);
+                return handleGivePhysicalAddress(message);
             case Constants.MESSAGE_GIVE_OSD_NAME:
                 return handleGiveOsdName(message);
             case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
-                return handleGiveDeviceVendorId(null);
+                return handleGiveDeviceVendorId(message);
             case Constants.MESSAGE_CEC_VERSION:
                 return handleCecVersion();
             case Constants.MESSAGE_GET_CEC_VERSION:
@@ -380,25 +380,32 @@
 
     @ServiceThreadOnly
     @Constants.HandleMessageResult
-    protected int handleGivePhysicalAddress(@Nullable SendMessageCallback callback) {
+    protected int handleGivePhysicalAddress(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         int physicalAddress = mService.getPhysicalAddress();
-        HdmiCecMessage cecMessage =
-                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
-                        mAddress, physicalAddress, mDeviceType);
-        mService.sendCecCommand(cecMessage, callback);
+        if (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+        } else {
+            HdmiCecMessage cecMessage =
+                    HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                            mAddress, physicalAddress, mDeviceType);
+            mService.sendCecCommand(cecMessage);
+        }
         return Constants.HANDLED;
     }
 
     @ServiceThreadOnly
     @Constants.HandleMessageResult
-    protected int handleGiveDeviceVendorId(@Nullable SendMessageCallback callback) {
+    protected int handleGiveDeviceVendorId(HdmiCecMessage message) {
         assertRunOnServiceThread();
         int vendorId = mService.getVendorId();
-        HdmiCecMessage cecMessage =
-                HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
-        mService.sendCecCommand(cecMessage, callback);
+        if (vendorId == Result.FAILURE_UNKNOWN) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+        } else {
+            HdmiCecMessage cecMessage =
+                    HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
+            mService.sendCecCommand(cecMessage);
+        }
         return Constants.HANDLED;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0bb1285..93b0560 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -34,7 +34,6 @@
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager.TvInputCallback;
 import android.os.SystemProperties;
-import android.provider.Settings.Global;
 import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
@@ -108,10 +107,12 @@
 
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
-        mRoutingControlFeatureEnabled =
-            mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
-        mSystemAudioControlFeatureEnabled =
-            mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+        mRoutingControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+                    == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+        mSystemAudioControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+                    == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
     }
 
     private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
@@ -857,7 +858,7 @@
         HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
     }
 
-    void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+    void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
         setSystemAudioControlFeatureEnabled(enabled);
         if (enabled) {
             addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
@@ -873,7 +874,7 @@
     }
 
     @ServiceThreadOnly
-    void setRoutingControlFeatureEnables(boolean enabled) {
+    void setRoutingControlFeatureEnabled(boolean enabled) {
         assertRunOnServiceThread();
         synchronized (mLock) {
             mRoutingControlFeatureEnabled = enabled;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 37ee76b..1276aa3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -140,13 +140,10 @@
         // Invalidate the internal active source record when going to standby
         mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
                 "HdmiCecLocalDevicePlayback#onStandby()");
-        boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
-                    == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
         if (!wasActiveSource) {
             return;
         }
-        if (initiatedByCec || !mTvSendStandbyOnSleep) {
+        if (initiatedByCec) {
             mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
                             mService.getPhysicalAddress()));
             return;
@@ -155,13 +152,20 @@
             case HdmiControlService.STANDBY_SCREEN_OFF:
                 // Get latest setting value
                 @HdmiControlManager.PowerControlMode
-                String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+                String powerControlMode = mService.getHdmiCecConfig().getStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
-                switch (sendStandbyOnSleep) {
+                switch (powerControlMode) {
                     case HdmiControlManager.POWER_CONTROL_MODE_TV:
                         mService.sendCecCommand(
                                 HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
                         break;
+                    case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
+                        mService.sendCecCommand(
+                                HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+                        mService.sendCecCommand(
+                                HdmiCecMessageBuilder.buildStandby(mAddress,
+                                        Constants.ADDR_AUDIO_SYSTEM));
+                        break;
                     case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
                         mService.sendCecCommand(
                                 HdmiCecMessageBuilder.buildStandby(mAddress,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 1c726e0..0c7b3f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -107,14 +107,18 @@
     @ServiceThreadOnly
     protected void sendStandby(int deviceId) {
         assertRunOnServiceThread();
-        String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+        String powerControlMode = mService.getHdmiCecConfig().getStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
-        if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
+        if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
             mService.sendCecCommand(
                     HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
             return;
         }
         mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+        if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_AUDIO_SYSTEM));
+        }
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index acfeb6c..1d099da 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -38,11 +38,9 @@
 import android.hardware.hdmi.HdmiTimerRecordSources;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager.TvInputCallback;
-import android.provider.Settings.Global;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 
@@ -155,8 +153,9 @@
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_TV);
         mPrevPortId = Constants.INVALID_PORT_ID;
-        mSystemAudioControlFeatureEnabled =
-                mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+        mSystemAudioControlFeatureEnabled = service.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+                    == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
         mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
     }
 
@@ -374,12 +373,11 @@
             return;
         }
         int newPath = mService.portIdToPath(portId);
-        startRoutingControl(oldPath, newPath, true, callback);
+        startRoutingControl(oldPath, newPath, callback);
     }
 
     @ServiceThreadOnly
-    void startRoutingControl(int oldPath, int newPath, boolean queryDevicePowerStatus,
-            IHdmiControlCallback callback) {
+    void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (oldPath == newPath) {
             return;
@@ -389,7 +387,7 @@
         mService.sendCecCommand(routingChange);
         removeAction(RoutingControlAction.class);
         addAndStartAction(
-                new RoutingControlAction(this, newPath, queryDevicePowerStatus, callback));
+                new RoutingControlAction(this, newPath, callback));
     }
 
     @ServiceThreadOnly
@@ -411,6 +409,11 @@
     }
 
     @Override
+    protected int findAudioReceiverAddress() {
+        return Constants.ADDR_AUDIO_SYSTEM;
+    }
+
+    @Override
     @ServiceThreadOnly
     @Constants.HandleMessageResult
     protected int handleActiveSource(HdmiCecMessage message) {
@@ -567,7 +570,7 @@
         if (isTailOfActivePath(path, getActivePath())) {
             int newPath = mService.portIdToPath(getActivePortId());
             setActivePath(newPath);
-            startRoutingControl(getActivePath(), newPath, false, null);
+            startRoutingControl(getActivePath(), newPath, null);
             return true;
         }
         return false;
@@ -612,7 +615,7 @@
             getActiveSource().invalidate();
             removeAction(RoutingControlAction.class);
             int newPath = HdmiUtils.twoBytesToInt(params, 2);
-            addAndStartAction(new RoutingControlAction(this, newPath, true, null));
+            addAndStartAction(new RoutingControlAction(this, newPath, null));
         }
         return Constants.HANDLED;
     }
@@ -926,10 +929,6 @@
         synchronized (mLock) {
             mSystemAudioMute = mute;
             mSystemAudioVolume = volume;
-            int maxVolume = mService.getAudioManager().getStreamMaxVolume(
-                    AudioManager.STREAM_MUSIC);
-            mService.setAudioStatus(mute,
-                    VolumeControlAction.scaleToCustomVolume(volume, maxVolume));
             displayOsd(HdmiControlManager.OSD_MESSAGE_AVR_VOLUME_CHANGED,
                     mute ? HdmiControlManager.AVR_VOLUME_MUTED : volume);
         }
@@ -1189,7 +1188,7 @@
         // Seq #23
         if (isTailOfActivePath(path, getActivePath())) {
             int newPath = mService.portIdToPath(getActivePortId());
-            startRoutingControl(getActivePath(), newPath, true, null);
+            startRoutingControl(getActivePath(), newPath, null);
         }
     }
 
@@ -1207,7 +1206,7 @@
             if (!routingForBootup && !isProhibitMode()) {
                 int newPath = mService.portIdToPath(getActivePortId());
                 setActivePath(newPath);
-                startRoutingControl(getActivePath(), newPath, routingForBootup, null);
+                startRoutingControl(getActivePath(), newPath, null);
             }
         } else {
             int activePath = mService.getPhysicalAddress();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c85fd50..15a3167 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -119,6 +119,10 @@
         if (mParams.length > 0) {
             if (filterMessageParameters(mOpcode)) {
                 s.append(String.format(" <Redacted len=%d>", mParams.length));
+            } else if (isUserControlPressedMessage(mOpcode)) {
+                s.append(
+                        String.format(
+                                " <Keycode type = %s>", HdmiCecKeycode.getKeycodeType(mParams[0])));
             } else {
                 for (byte data : mParams) {
                     s.append(String.format(":%02X", data));
@@ -287,7 +291,6 @@
 
     private static boolean filterMessageParameters(int opcode) {
         switch (opcode) {
-            case Constants.MESSAGE_USER_CONTROL_PRESSED:
             case Constants.MESSAGE_USER_CONTROL_RELEASED:
             case Constants.MESSAGE_SET_OSD_NAME:
             case Constants.MESSAGE_SET_OSD_STRING:
@@ -300,5 +303,9 @@
                 return false;
         }
     }
+
+    private static boolean isUserControlPressedMessage(int opcode) {
+        return Constants.MESSAGE_USER_CONTROL_PRESSED == opcode;
+    }
 }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a086bda..c7d0e9e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -618,6 +618,53 @@
                         initializeCec(INITIATED_BY_ENABLE_CEC);
                     }
                 }, mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        boolean enabled = mHdmiCecConfig.getIntValue(
+                                HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+                                    == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+                        if (isAudioSystemDevice()) {
+                            if (audioSystem() == null) {
+                                Slog.w(TAG, "Switch device has not registered yet."
+                                        + " Can't turn routing on.");
+                            } else {
+                                audioSystem().setRoutingControlFeatureEnabled(enabled);
+                            }
+                        }
+                    }
+                }, mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        boolean enabled = mHdmiCecConfig.getIntValue(
+                                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+                                    == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
+                        if (isTvDeviceEnabled()) {
+                            tv().setSystemAudioControlFeatureEnabled(enabled);
+                        }
+                        if (isAudioSystemDevice()) {
+                            if (audioSystem() == null) {
+                                Slog.e(TAG, "Audio System device has not registered yet."
+                                        + " Can't turn system audio mode on.");
+                            } else {
+                                audioSystem().onSystemAudioControlFeatureSupportChanged(enabled);
+                            }
+                        }
+                    }
+                }, mServiceThreadExecutor);
+        mHdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                new HdmiCecConfig.SettingChangeListener() {
+                    @Override
+                    public void onChange(String setting) {
+                        setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+                                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
+                    }
+                }, mServiceThreadExecutor);
         mHdmiCecConfig.registerChangeListener(
                 HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
                 new HdmiCecConfig.SettingChangeListener() {
@@ -757,11 +804,8 @@
     private void registerContentObserver() {
         ContentResolver resolver = getContext().getContentResolver();
         String[] settings = new String[] {
-                Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
-                Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
                 Global.MHL_POWER_CHARGE_ENABLED,
-                Global.HDMI_CEC_SWITCH_ENABLED,
                 Global.DEVICE_NAME
         };
         for (String s : settings) {
@@ -781,33 +825,6 @@
             String option = uri.getLastPathSegment();
             boolean enabled = readBooleanSetting(option, true);
             switch (option) {
-                case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
-                    setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
-                            HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
-                    break;
-                case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
-                    if (isTvDeviceEnabled()) {
-                        tv().setSystemAudioControlFeatureEnabled(enabled);
-                    }
-                    if (isAudioSystemDevice()) {
-                        if (audioSystem() == null) {
-                            Slog.e(TAG, "Audio System device has not registered yet."
-                                    + " Can't turn system audio mode on.");
-                            break;
-                        }
-                        audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
-                    }
-                    break;
-                case Global.HDMI_CEC_SWITCH_ENABLED:
-                    if (isAudioSystemDevice()) {
-                        if (audioSystem() == null) {
-                            Slog.w(TAG, "Switch device has not registered yet."
-                                    + " Can't turn routing on.");
-                            break;
-                        }
-                        audioSystem().setRoutingControlFeatureEnables(enabled);
-                    }
-                    break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
                     setMhlInputChangeEnabled(enabled);
                     break;
@@ -2359,7 +2376,7 @@
         @Override
         public List<String> getUserCecSettings() {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
             } finally {
@@ -2370,7 +2387,7 @@
         @Override
         public List<String> getAllowedCecSettingStringValues(String name) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
             } finally {
@@ -2381,7 +2398,7 @@
         @Override
         public int[] getAllowedCecSettingIntValues(String name) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 List<Integer> allowedValues =
                         HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
@@ -2394,7 +2411,7 @@
         @Override
         public String getCecSettingStringValue(String name) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
             } finally {
@@ -2405,7 +2422,7 @@
         @Override
         public void setCecSettingStringValue(String name, String value) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
             } finally {
@@ -2416,7 +2433,7 @@
         @Override
         public int getCecSettingIntValue(String name) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
             } finally {
@@ -2427,7 +2444,7 @@
         @Override
         public void setCecSettingIntValue(String name, int value) {
             initBinderCall();
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
             } finally {
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 979e7a4..3dcf72e 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -178,10 +178,11 @@
         if (service.isAudioSystemDevice()) {
             return false;
         }
-        @HdmiControlManager.PowerControlMode String sendStandbyOnSleep =
+        @HdmiControlManager.PowerControlMode String powerControlMode =
                 service.getHdmiCecConfig().getStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
-        return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+        return powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)
+                || powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
     private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index b9404e4..0c4fb26 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,11 +17,10 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.util.Slog;
 
-import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Feature action for routing control. Exchanges routing-related commands with other devices
@@ -41,23 +40,12 @@
 
     // State in which we wait for <Routing Information> to arrive. If timed out, we use the
     // latest routing path to set the new active source.
-    private static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
-
-    // State in which we wait for <Report Power Status> in response to <Give Device Power Status>
-    // we have sent. If the response tells us the device power is on, we send <Set Stream Path>
-    // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly
-    // just show the blank screen.
-    private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+    @VisibleForTesting
+    static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
 
     // Time out in millseconds used for <Routing Information>
     private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
 
-    // Time out in milliseconds used for <Report Power Status>
-    private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
-
-    // true if <Give Power Status> should be sent once the new active routing path is determined.
-    private final boolean mQueryDevicePowerStatus;
-
     // If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
     // the routing control/active source change happens. The listener should be called if
     // the events are triggered by external events such as manual switch port change or incoming
@@ -67,11 +55,9 @@
     // The latest routing path. Updated by each <Routing Information> from CEC switches.
     private int mCurrentRoutingPath;
 
-    RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus,
-            IHdmiControlCallback callback) {
+    RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
         super(localDevice, callback);
         mCurrentRoutingPath = path;
-        mQueryDevicePowerStatus = queryDevicePowerStatus;
         // Callback is non-null when routing control action is brought up by binder API. Use
         // this as an indicator for the input change notification. These API calls will get
         // the result through this callback, not through notification. Any other events that
@@ -104,39 +90,16 @@
             removeActionExcept(RoutingControlAction.class, this);
             addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
             return true;
-        } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
-                  && opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
-            handleReportPowerStatus(cmd.getParams()[0]);
-            return true;
         }
         return false;
     }
 
-    private void handleReportPowerStatus(int devicePowerStatus) {
-        if (isPowerOnOrTransient(getTvPowerStatus())) {
-            updateActiveInput();
-            if (isPowerOnOrTransient(devicePowerStatus)) {
-                sendSetStreamPath();
-            }
-        }
-        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
-    }
-
     private void updateActiveInput() {
         HdmiCecLocalDeviceTv tv = tv();
         tv.setPrevPortId(tv.getActivePortId());
         tv.updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
     }
 
-    private int getTvPowerStatus() {
-        return tv().getPowerStatus();
-    }
-
-    private static boolean isPowerOnOrTransient(int status) {
-        return status == HdmiControlManager.POWER_STATUS_ON
-                || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
-    }
-
     private void sendSetStreamPath() {
         sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
                 mCurrentRoutingPath));
@@ -150,46 +113,13 @@
         }
         switch (timeoutState) {
             case STATE_WAIT_FOR_ROUTING_INFORMATION:
-                HdmiDeviceInfo device =
-                        localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
-                                mCurrentRoutingPath);
-                if (device != null && mQueryDevicePowerStatus) {
-                    int deviceLogicalAddress = device.getLogicalAddress();
-                    queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
-                        @Override
-                        public void onSendCompleted(int error) {
-                            handlDevicePowerStatusAckResult(
-                                    error == HdmiControlManager.RESULT_SUCCESS);
-                        }
-                    });
-                } else {
-                    updateActiveInput();
-                    finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
-                }
-                return;
-            case STATE_WAIT_FOR_REPORT_POWER_STATUS:
-                if (isPowerOnOrTransient(getTvPowerStatus())) {
-                    updateActiveInput();
-                    sendSetStreamPath();
-                }
+                updateActiveInput();
+                sendSetStreamPath();
                 finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
                 return;
-        }
-    }
-
-    private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
-        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
-                callback);
-    }
-
-    private void handlDevicePowerStatusAckResult(boolean acked) {
-        if (acked) {
-            mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
-            addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
-        } else {
-            updateActiveInput();
-            sendSetStreamPath();
-            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+            default:
+                Slog.e("CEC", "Invalid timeoutState (" + timeoutState + ").");
+                return;
         }
     }
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9fcc9a1..d3083ca 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2856,8 +2856,7 @@
         };
         for (File baseDir: baseDirs) {
             File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
-            try {
-                InputStream stream = new FileInputStream(confFile);
+            try (InputStream stream = new FileInputStream(confFile)) {
                 names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
             } catch (FileNotFoundException e) {
                 // It's ok if the file does not exist.
@@ -2890,8 +2889,7 @@
         final File baseDir = Environment.getVendorDirectory();
         final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
 
-        try {
-            final InputStream stream = new FileInputStream(confFile);
+        try (final InputStream stream = new FileInputStream(confFile)) {
             return ConfigurationProcessor.processInputPortAssociations(stream);
         } catch (FileNotFoundException e) {
             // Most of the time, file will not exist, which is expected.
@@ -3012,10 +3010,10 @@
             @Override
             public void visitKeyboardLayout(Resources resources,
                     int keyboardLayoutResId, KeyboardLayout layout) {
-                try {
+                try (final InputStreamReader stream = new InputStreamReader(
+                               resources.openRawResource(keyboardLayoutResId))) {
                     result[0] = layout.getDescriptor();
-                    result[1] = Streams.readFully(new InputStreamReader(
-                            resources.openRawResource(keyboardLayoutResId)));
+                    result[1] = Streams.readFully(stream);
                 } catch (IOException ex) {
                 } catch (NotFoundException ex) {
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fd0f1c3..de78bec 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -44,7 +44,6 @@
 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.util.imetracing.ImeTracing.IME_TRACING_FROM_IMMS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
@@ -124,7 +123,6 @@
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
-import android.util.imetracing.ImeTracing;
 import android.util.proto.ProtoOutputStream;
 import android.view.IWindowManager;
 import android.view.InputChannel;
@@ -160,6 +158,7 @@
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.IVoidResultCallback;
+import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
@@ -2528,9 +2527,8 @@
                 }
                 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                 // Dispatch display id for InputMethodService to update context display.
-                executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
-                        MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
-                        mMethodMap.get(mCurMethodId).getConfigChanges()));
+                executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_INITIALIZE_IME,
+                        mMethodMap.get(mCurMethodId).getConfigChanges(), mCurMethod, mCurToken));
                 scheduleNotifyImeUidToAudioService(mCurMethodUid);
                 if (mCurClient != null) {
                     clearClientSessionLocked(mCurClient);
@@ -3928,7 +3926,7 @@
     @BinderThread
     @Override
     public void startProtoDump(byte[] protoDump, int source, String where) {
-        if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
+        if (protoDump == null && source != ImeTracing.IME_TRACING_FROM_IMMS) {
             // Dump not triggered from IMMS, but no proto information provided.
             return;
         }
@@ -3955,7 +3953,7 @@
                 proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
                 proto.end(service_token);
                 break;
-            case IME_TRACING_FROM_IMMS:
+            case ImeTracing.IME_TRACING_FROM_IMMS:
                 final long managerservice_token =
                         proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
                 proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
@@ -4292,12 +4290,11 @@
                 try {
                     if (DEBUG) {
                         Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
-                                + msg.arg1);
+                                + mCurTokenDisplayId);
                     }
                     final IBinder token = (IBinder) args.arg2;
-                    ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
-                            new InputMethodPrivilegedOperationsImpl(this, token),
-                            (int) args.arg3);
+                    ((IInputMethod) args.arg1).initializeInternal(token,
+                            new InputMethodPrivilegedOperationsImpl(this, token), msg.arg1);
                 } catch (RemoteException e) {
                 }
                 args.recycle();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java b/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
deleted file mode 100644
index a6a6893..0000000
--- a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
+++ /dev/null
@@ -1,67 +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.server.inputmethod;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Build;
-import android.os.SystemProperties;
-
-/**
- * Various (pseudo) constants about IME behaviors.
- */
-public class InputMethodSystemProperty {
-    /**
-     * System property key for the production use. The value must be either empty or a valid
-     * (flattened) component name of the multi-client IME.
-     */
-    private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
-
-    /**
-     * System property key for debugging purpose. The value must be either empty or a valid
-     * (flattened) component name of the multi-client IME.
-     *
-     * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
-     */
-    private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
-
-    @Nullable
-    private static ComponentName getMultiClientImeComponentName() {
-        if (Build.IS_DEBUGGABLE) {
-            // If debuggable, allow developers to override the multi-client IME component name
-            // with a different (writable) key.
-            final ComponentName debugIme = ComponentName.unflattenFromString(
-                    SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
-            if (debugIme != null) {
-                return debugIme;
-            }
-        }
-        return ComponentName.unflattenFromString(
-                SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
-    }
-
-    /**
-     * {@link ComponentName} of multi-client IME to be used.
-     */
-    @Nullable
-    static final ComponentName sMultiClientImeComponentName = getMultiClientImeComponentName();
-
-    /**
-     * {@code true} when multi-client IME is enabled.
-     */
-    public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
-}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
deleted file mode 100644
index ce195e6..0000000
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ /dev/null
@@ -1,1858 +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.server.inputmethod;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.AnyThread;
-import android.annotation.BinderThread;
-import android.annotation.IntDef;
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.annotation.WorkerThread;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-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.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.inputmethodservice.MultiClientInputMethodServiceDelegate;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.inputmethod.StartInputFlags;
-import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.inputmethod.UnbindReason;
-import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputBindResult;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.wm.WindowManagerInternal;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.util.Collections;
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * Actual implementation of multi-client InputMethodManagerService.
- *
- * <p>This system service is intentionally compatible with {@link InputMethodManagerService} so that
- * we can switch the implementation at the boot time.</p>
- */
-public final class MultiClientInputMethodManagerService {
-    private static final String TAG = "MultiClientInputMethodManagerService";
-    private static final boolean DEBUG = false;
-
-    private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE =
-            "config_perDisplayFocusEnabled is not true.";
-
-    private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG =
-            "Consider rebuilding the system image after enabling config_perDisplayFocusEnabled to "
-                    + "make IME focus compatible with multi-client IME mode.";
-
-    private static final long RECONNECT_DELAY_MSEC = 1000;
-
-    /**
-     * Unlike {@link InputMethodManagerService}, {@link MultiClientInputMethodManagerService}
-     * always binds to the IME with {@link Context#BIND_FOREGROUND_SERVICE} for now for simplicity.
-     */
-    private static final int IME_CONNECTION_UNIFIED_BIND_FLAGS =
-            Context.BIND_AUTO_CREATE
-                    | Context.BIND_NOT_VISIBLE
-                    | Context.BIND_NOT_FOREGROUND
-                    | Context.BIND_FOREGROUND_SERVICE;
-
-    private static final ComponentName sImeComponentName =
-            InputMethodSystemProperty.sMultiClientImeComponentName;
-
-    private static void reportNotSupported() {
-        if (DEBUG) {
-            Slog.d(TAG, "non-supported operation. callers=" + Debug.getCallers(3));
-        }
-    }
-
-    /**
-     * {@link MultiClientInputMethodManagerService} is not intended to be instantiated.
-     */
-    private MultiClientInputMethodManagerService() {
-    }
-
-    /**
-     * The implementation of {@link SystemService} for multi-client IME.
-     */
-    public static final class Lifecycle extends SystemService {
-        private final ApiCallbacks mApiCallbacks;
-        private final OnWorkerThreadCallback mOnWorkerThreadCallback;
-
-        @MainThread
-        public Lifecycle(Context context) {
-            super(context);
-
-            final UserToInputMethodInfoMap userIdToInputMethodInfoMapper =
-                    new UserToInputMethodInfoMap();
-            final UserDataMap userDataMap = new UserDataMap();
-            final HandlerThread workerThread = new HandlerThread(TAG);
-            workerThread.start();
-            mApiCallbacks = new ApiCallbacks(context, userDataMap, userIdToInputMethodInfoMapper);
-            mOnWorkerThreadCallback = new OnWorkerThreadCallback(
-                    context, userDataMap, userIdToInputMethodInfoMapper,
-                    new Handler(workerThread.getLooper(), msg -> false, true));
-
-            LocalServices.addService(InputMethodManagerInternal.class,
-                    new InputMethodManagerInternal() {
-                        @Override
-                        public void setInteractive(boolean interactive) {
-                            reportNotSupported();
-                        }
-
-                        @Override
-                        public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
-                            reportNotSupported();
-                        }
-
-                        @Override
-                        public List<InputMethodInfo> getInputMethodListAsUser(
-                                @UserIdInt int userId) {
-                            return userIdToInputMethodInfoMapper.getAsList(userId);
-                        }
-
-                        @Override
-                        public List<InputMethodInfo> getEnabledInputMethodListAsUser(
-                                @UserIdInt int userId) {
-                            return userIdToInputMethodInfoMapper.getAsList(userId);
-                        }
-
-                        @Override
-                        public void onCreateInlineSuggestionsRequest(int userId,
-                                InlineSuggestionsRequestInfo requestInfo,
-                                IInlineSuggestionsRequestCallback cb) {
-                            try {
-                                cb.onInlineSuggestionsUnsupported();
-                            } catch (RemoteException e) {
-                                Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
-                            }
-                        }
-
-                        @Override
-                        public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
-                            reportNotSupported();
-                            return false;
-                        }
-
-                        @Override
-                        public void registerInputMethodListListener(
-                                InputMethodListListener listener) {
-                            reportNotSupported();
-                        }
-
-                        @Override
-                        public boolean transferTouchFocusToImeWindow(
-                                @NonNull IBinder sourceInputToken, int displayId) {
-                            reportNotSupported();
-                            return false;
-                        }
-
-                        @Override
-                        public void reportImeControl(@Nullable IBinder windowToken) {
-                        }
-
-                        @Override
-                        public void removeImeSurface() {
-                            reportNotSupported();
-                        }
-
-                        @Override
-                        public void updateImeWindowStatus(boolean disableImeIcon) {
-                        }
-                    });
-        }
-
-        @MainThread
-        @Override
-        public void onBootPhase(int phase) {
-            mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
-                    OnWorkerThreadCallback::onBootPhase, mOnWorkerThreadCallback, phase));
-        }
-
-        @MainThread
-        @Override
-        public void onStart() {
-            publishBinderService(Context.INPUT_METHOD_SERVICE, mApiCallbacks);
-        }
-
-        @MainThread
-        @Override
-        public void onUserStarting(@NonNull TargetUser user) {
-            mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
-                    OnWorkerThreadCallback::onStartUser, mOnWorkerThreadCallback,
-                    user.getUserIdentifier()));
-        }
-
-        @MainThread
-        @Override
-        public void onUserUnlocking(@NonNull TargetUser user) {
-            mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
-                    OnWorkerThreadCallback::onUnlockUser, mOnWorkerThreadCallback,
-                    user.getUserIdentifier()));
-        }
-
-        @MainThread
-        @Override
-        public void onUserStopping(@NonNull TargetUser user) {
-            mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
-                    OnWorkerThreadCallback::onStopUser, mOnWorkerThreadCallback,
-                    user.getUserIdentifier()));
-        }
-    }
-
-    private static final class OnWorkerThreadCallback {
-        private final Context mContext;
-        private final UserDataMap mUserDataMap;
-        private final UserToInputMethodInfoMap mInputMethodInfoMap;
-        private final Handler mHandler;
-
-        OnWorkerThreadCallback(Context context, UserDataMap userDataMap,
-                UserToInputMethodInfoMap inputMethodInfoMap, Handler handler) {
-            mContext = context;
-            mUserDataMap = userDataMap;
-            mInputMethodInfoMap = inputMethodInfoMap;
-            mHandler = handler;
-        }
-
-        @AnyThread
-        Handler getHandler() {
-            return mHandler;
-        }
-
-        @WorkerThread
-        private void tryBindInputMethodService(@UserIdInt int userId) {
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.i(TAG, "tryBindInputMethodService is called for an unknown user=" + userId);
-                return;
-            }
-
-            final InputMethodInfo imi = queryInputMethod(mContext, userId, sImeComponentName);
-            if (imi == null) {
-                Slog.w(TAG, "Multi-client InputMethod is not found. component="
-                        + sImeComponentName);
-                synchronized (data.mLock) {
-                    switch (data.mState) {
-                        case PerUserState.USER_LOCKED:
-                        case PerUserState.SERVICE_NOT_QUERIED:
-                        case PerUserState.SERVICE_RECOGNIZED:
-                        case PerUserState.UNBIND_CALLED:
-                            // Safe to clean up.
-                            mInputMethodInfoMap.remove(userId);
-                            break;
-                    }
-                }
-                return;
-            }
-
-            synchronized (data.mLock) {
-                switch (data.mState) {
-                    case PerUserState.USER_LOCKED:
-                        // If the user is still locked, we currently do not try to start IME.
-                        return;
-                    case PerUserState.SERVICE_NOT_QUERIED:
-                    case PerUserState.SERVICE_RECOGNIZED:
-                    case PerUserState.UNBIND_CALLED:
-                        break;
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                    case PerUserState.SERVICE_CONNECTED:
-                        // OK, nothing to do.
-                        return;
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        return;
-                }
-                data.mState = PerUserState.SERVICE_RECOGNIZED;
-                data.mCurrentInputMethodInfo = imi;
-                mInputMethodInfoMap.put(userId, imi);
-                final boolean bindResult = data.bindServiceLocked(mContext, userId);
-                if (!bindResult) {
-                    Slog.e(TAG, "Failed to bind Multi-client InputMethod.");
-                    return;
-                }
-                data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
-            }
-        }
-
-        @WorkerThread
-        void onStartUser(@UserIdInt int userId) {
-            if (DEBUG) {
-                Slog.v(TAG, "onStartUser userId=" + userId);
-            }
-            final PerUserData data = new PerUserData(userId, null, PerUserState.USER_LOCKED, this);
-            mUserDataMap.put(userId, data);
-        }
-
-        @WorkerThread
-        void onUnlockUser(@UserIdInt int userId) {
-            if (DEBUG) {
-                Slog.v(TAG, "onUnlockUser() userId=" + userId);
-            }
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.i(TAG, "onUnlockUser is called for an unknown user=" + userId);
-                return;
-            }
-            synchronized (data.mLock) {
-                switch (data.mState) {
-                    case PerUserState.USER_LOCKED:
-                        data.mState = PerUserState.SERVICE_NOT_QUERIED;
-                        tryBindInputMethodService(userId);
-                        break;
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        break;
-                }
-            }
-        }
-
-        @WorkerThread
-        void onStopUser(@UserIdInt int userId) {
-            if (DEBUG) {
-                Slog.v(TAG, "onStopUser() userId=" + userId);
-            }
-            mInputMethodInfoMap.remove(userId);
-            final PerUserData data = mUserDataMap.removeReturnOld(userId);
-            if (data == null) {
-                Slog.i(TAG, "onStopUser is called for an unknown user=" + userId);
-                return;
-            }
-            synchronized (data.mLock) {
-                switch (data.mState) {
-                    case PerUserState.USER_LOCKED:
-                    case PerUserState.SERVICE_RECOGNIZED:
-                    case PerUserState.UNBIND_CALLED:
-                        // OK, nothing to do.
-                        return;
-                    case PerUserState.SERVICE_CONNECTED:
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                        break;
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        break;
-                }
-                data.unbindServiceLocked(mContext);
-                data.mState = PerUserState.UNBIND_CALLED;
-                data.mCurrentInputMethod = null;
-
-                // When a Service is explicitly unbound with Context.unbindService(),
-                // onServiceDisconnected() will not be triggered.  Hence here we explicitly call
-                // onInputMethodDisconnectedLocked() as if the Service is already gone.
-                data.onInputMethodDisconnectedLocked();
-            }
-        }
-
-        @WorkerThread
-        void onServiceConnected(PerUserData data, IMultiClientInputMethod service) {
-            if (DEBUG) {
-                Slog.v(TAG, "onServiceConnected() data.mUserId=" + data.mUserId);
-            }
-            synchronized (data.mLock) {
-                switch (data.mState) {
-                    case PerUserState.UNBIND_CALLED:
-                        // We should ignore this callback.
-                        return;
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                        // OK.
-                        data.mState = PerUserState.SERVICE_CONNECTED;
-                        data.mCurrentInputMethod = service;
-                        try {
-                            data.mCurrentInputMethod.initialize(new ImeCallbacks(data));
-                        } catch (RemoteException e) {
-                        }
-                        data.onInputMethodConnectedLocked();
-                        break;
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        return;
-                }
-            }
-        }
-
-        @WorkerThread
-        void onServiceDisconnected(PerUserData data) {
-            if (DEBUG) {
-                Slog.v(TAG, "onServiceDisconnected() data.mUserId=" + data.mUserId);
-            }
-            final WindowManagerInternal windowManagerInternal =
-                    LocalServices.getService(WindowManagerInternal.class);
-            synchronized (data.mLock) {
-                // We assume the number of tokens would not be that large (up to 10 or so) hence
-                // linear search should be acceptable.
-                final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
-                for (int i = 0; i < numTokens; ++i) {
-                    final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
-                    windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
-                }
-                data.mDisplayIdToImeWindowTokenMap.clear();
-                switch (data.mState) {
-                    case PerUserState.UNBIND_CALLED:
-                        // We should ignore this callback.
-                        return;
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                    case PerUserState.SERVICE_CONNECTED:
-                        // onServiceDisconnected() means the biding is still alive.
-                        data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
-                        data.mCurrentInputMethod = null;
-                        data.onInputMethodDisconnectedLocked();
-                        break;
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        return;
-                }
-            }
-        }
-
-        @WorkerThread
-        void onBindingDied(PerUserData data) {
-            if (DEBUG) {
-                Slog.v(TAG, "onBindingDied() data.mUserId=" + data.mUserId);
-            }
-            final WindowManagerInternal windowManagerInternal =
-                    LocalServices.getService(WindowManagerInternal.class);
-            synchronized (data.mLock) {
-                // We assume the number of tokens would not be that large (up to 10 or so) hence
-                // linear search should be acceptable.
-                final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
-                for (int i = 0; i < numTokens; ++i) {
-                    final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
-                    windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
-                }
-                data.mDisplayIdToImeWindowTokenMap.clear();
-                switch (data.mState) {
-                    case PerUserState.UNBIND_CALLED:
-                        // We should ignore this callback.
-                        return;
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                    case PerUserState.SERVICE_CONNECTED: {
-                        // onBindingDied() means the biding is dead.
-                        data.mState = PerUserState.UNBIND_CALLED;
-                        data.mCurrentInputMethod = null;
-                        data.onInputMethodDisconnectedLocked();
-                        // Schedule a retry
-                        mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
-                                OnWorkerThreadCallback::tryBindInputMethodService,
-                                this, data.mUserId), RECONNECT_DELAY_MSEC);
-                        break;
-                    }
-                    default:
-                        Slog.wtf(TAG, "Unknown state=" + data.mState);
-                        return;
-                }
-            }
-        }
-
-        @WorkerThread
-        void onBootPhase(int phase) {
-            if (DEBUG) {
-                Slog.v(TAG, "onBootPhase() phase=" + phase);
-            }
-            switch (phase) {
-                case SystemService.PHASE_ACTIVITY_MANAGER_READY: {
-                    final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-                    filter.addDataScheme("package");
-                    mContext.registerReceiver(new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            onPackageAdded(intent);
-                        }
-                    }, filter, null, mHandler);
-                    break;
-                }
-                case SystemService.PHASE_BOOT_COMPLETED: {
-                    final boolean perDisplayFocusEnabled = mContext.getResources().getBoolean(
-                            com.android.internal.R.bool.config_perDisplayFocusEnabled);
-                    if (!perDisplayFocusEnabled) {
-                        final Bundle extras = new Bundle();
-                        extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
-                        mContext.getSystemService(NotificationManager.class).notifyAsUser(TAG,
-                                SystemMessageProto.SystemMessage.NOTE_SELECT_INPUT_METHOD,
-                                new Notification.Builder(mContext,
-                                        SystemNotificationChannels.VIRTUAL_KEYBOARD)
-                                        .setContentTitle(PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE)
-                                        .setStyle(new Notification.BigTextStyle()
-                                                .bigText(PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG))
-                                        .setSmallIcon(R.drawable.ic_notification_ime_default)
-                                        .setWhen(0)
-                                        .setOngoing(true)
-                                        .setLocalOnly(true)
-                                        .addExtras(extras)
-                                        .setCategory(Notification.CATEGORY_SYSTEM)
-                                        .setColor(mContext.getColor(
-                                                R.color.system_notification_accent_color))
-                                        .build(), UserHandle.ALL);
-                    }
-                    break;
-                }
-            }
-        }
-
-        @WorkerThread
-        void onPackageAdded(Intent intent) {
-            if (DEBUG) {
-                Slog.v(TAG, "onPackageAdded() intent=" + intent);
-            }
-            final Uri uri = intent.getData();
-            if (uri == null) {
-                return;
-            }
-            if (!intent.hasExtra(Intent.EXTRA_UID)) {
-                return;
-            }
-            final String packageName = uri.getSchemeSpecificPart();
-            if (sImeComponentName == null
-                    || packageName == null
-                    || !TextUtils.equals(sImeComponentName.getPackageName(), packageName)) {
-                return;
-            }
-            final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0));
-            tryBindInputMethodService(userId);
-        }
-    }
-
-    private static final class WindowInfo {
-        final IBinder mWindowToken;
-        final int mWindowHandle;
-
-        WindowInfo(IBinder windowToken, int windowCookie) {
-            mWindowToken = windowToken;
-            mWindowHandle = windowCookie;
-        }
-    }
-
-    /**
-     * Describes the state of each IME client.
-     */
-    @Retention(SOURCE)
-    @IntDef({InputMethodClientState.REGISTERED,
-            InputMethodClientState.WAITING_FOR_IME_SESSION,
-            InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT,
-            InputMethodClientState.ALREADY_SENT_BIND_RESULT,
-            InputMethodClientState.UNREGISTERED})
-    private @interface InputMethodClientState {
-        /**
-         * {@link IInputMethodManager#addClient(IInputMethodClient, IInputContext, int)} is called
-         * and this client is now recognized by the system.  When the system lost the connection to
-         * the current IME, all the clients need to be re-initialized from this state.
-         */
-        int REGISTERED = 1;
-        /**
-         * This client is notified to the current IME with {@link
-         * IMultiClientInputMethod#addClient(int, int, int, int)} but the IME is not yet responded
-         * with {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
-         * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
-         */
-        int WAITING_FOR_IME_SESSION = 2;
-        /**
-         * This client is already accepted by the IME but a valid {@link InputBindResult} has not
-         * been returned to the client yet.
-         */
-        int READY_TO_SEND_FIRST_BIND_RESULT = 3;
-        /**
-         * This client has already received a valid {@link InputBindResult} at least once. This
-         * means that the client can directly call {@link IInputMethodSession} IPCs and key events
-         * via {@link InputChannel}. When the current IME is unbound, these client end points also
-         * need to be cleared.
-         */
-        int ALREADY_SENT_BIND_RESULT = 4;
-        /**
-         * The client process is dying.
-         */
-        int UNREGISTERED = 5;
-    }
-
-    private static final class InputMethodClientIdSource {
-        @GuardedBy("InputMethodClientIdSource.class")
-        private static int sNextValue = 0;
-
-        private InputMethodClientIdSource() {
-        }
-
-        static synchronized int getNext() {
-            final int result = sNextValue;
-            sNextValue++;
-            if (sNextValue < 0) {
-                sNextValue = 0;
-            }
-            return result;
-        }
-    }
-
-    private static final class WindowHandleSource {
-        @GuardedBy("WindowHandleSource.class")
-        private static int sNextValue = 0;
-
-        private WindowHandleSource() {
-        }
-
-        static synchronized int getNext() {
-            final int result = sNextValue;
-            sNextValue++;
-            if (sNextValue < 0) {
-                sNextValue = 0;
-            }
-            return result;
-        }
-    }
-
-    private static final class InputMethodClientInfo {
-        final IInputMethodClient mClient;
-        final int mUid;
-        final int mPid;
-        final int mSelfReportedDisplayId;
-        final int mClientId;
-
-        @GuardedBy("PerUserData.mLock")
-        @InputMethodClientState
-        int mState;
-        @GuardedBy("PerUserData.mLock")
-        int mBindingSequence;
-        @GuardedBy("PerUserData.mLock")
-        InputChannel mWriteChannel;
-        @GuardedBy("PerUserData.mLock")
-        IInputMethodSession mInputMethodSession;
-        @GuardedBy("PerUserData.mLock")
-        IMultiClientInputMethodSession mMSInputMethodSession;
-        @GuardedBy("PerUserData.mLock")
-        final WeakHashMap<IBinder, WindowInfo> mWindowMap = new WeakHashMap<>();
-
-        InputMethodClientInfo(IInputMethodClient client, int uid, int pid,
-                int selfReportedDisplayId) {
-            mClient = client;
-            mUid = uid;
-            mPid = pid;
-            mSelfReportedDisplayId = selfReportedDisplayId;
-            mClientId = InputMethodClientIdSource.getNext();
-        }
-
-        @GuardedBy("PerUserData.mLock")
-        void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
-            ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence
-                    + ",mWriteChannel=" + mWriteChannel
-                    + ",mInputMethodSession=" + mInputMethodSession
-                    + ",mMSInputMethodSession=" + mMSInputMethodSession);
-        }
-    }
-
-    private static final class UserDataMap {
-        @GuardedBy("mMap")
-        private final SparseArray<PerUserData> mMap = new SparseArray<>();
-
-        @AnyThread
-        @Nullable
-        PerUserData get(@UserIdInt int userId) {
-            synchronized (mMap) {
-                return mMap.get(userId);
-            }
-        }
-
-        @AnyThread
-        void put(@UserIdInt int userId, PerUserData data) {
-            synchronized (mMap) {
-                mMap.put(userId, data);
-            }
-        }
-
-        @AnyThread
-        @Nullable
-        PerUserData removeReturnOld(@UserIdInt int userId) {
-            synchronized (mMap) {
-                return mMap.removeReturnOld(userId);
-            }
-        }
-
-        @AnyThread
-        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
-            synchronized (mMap) {
-                for (int i = 0; i < mMap.size(); i++) {
-                    int userId = mMap.keyAt(i);
-                    PerUserData data = mMap.valueAt(i);
-                    ipw.println("userId=" + userId + ", data=");
-                    if (data != null) {
-                        ipw.increaseIndent();
-                        data.dump(fd, ipw, args);
-                        ipw.decreaseIndent();
-                    }
-                }
-            }
-        }
-    }
-
-    private static final class TokenInfo {
-        final Binder mToken;
-        final int mDisplayId;
-        TokenInfo(Binder token, int displayId) {
-            mToken = token;
-            mDisplayId = displayId;
-        }
-    }
-
-    @Retention(SOURCE)
-    @IntDef({
-            PerUserState.USER_LOCKED,
-            PerUserState.SERVICE_NOT_QUERIED,
-            PerUserState.SERVICE_RECOGNIZED,
-            PerUserState.WAITING_SERVICE_CONNECTED,
-            PerUserState.SERVICE_CONNECTED,
-            PerUserState.UNBIND_CALLED})
-    private @interface PerUserState {
-        /**
-         * The user is still locked.
-         */
-        int USER_LOCKED = 1;
-        /**
-         * The system has not queried whether there is a multi-client IME or not.
-         */
-        int SERVICE_NOT_QUERIED = 2;
-        /**
-         * A multi-client IME specified in {@link #PROP_DEBUG_MULTI_CLIENT_IME} is found in the
-         * system, but not bound yet.
-         */
-        int SERVICE_RECOGNIZED = 3;
-        /**
-         * {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle)} is
-         * already called for the IME but
-         * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is not yet called
-         * back. This includes once the IME is bound but temporarily disconnected as notified with
-         * {@link ServiceConnection#onServiceDisconnected(ComponentName)}.
-         */
-        int WAITING_SERVICE_CONNECTED = 4;
-        /**
-         * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is already called
-         * back. The IME is ready to be used.
-         */
-        int SERVICE_CONNECTED = 5;
-        /**
-         * The binding is gone.  Either {@link Context#unbindService(ServiceConnection)} is
-         * explicitly called or the system decided to destroy the binding as notified with
-         * {@link ServiceConnection#onBindingDied(ComponentName)}.
-         */
-        int UNBIND_CALLED = 6;
-    }
-
-    /**
-     * Takes care of per-user state separation.
-     */
-    private static final class PerUserData {
-        final Object mLock = new Object();
-
-        /**
-         * User ID (not UID) that is associated with this data.
-         */
-        @UserIdInt
-        private final int mUserId;
-
-        /**
-         * {@link IMultiClientInputMethod} of the currently connected multi-client IME.  This
-         * must be non-{@code null} only while {@link #mState} is
-         * {@link PerUserState#SERVICE_CONNECTED}.
-         */
-        @Nullable
-        @GuardedBy("mLock")
-        IMultiClientInputMethod mCurrentInputMethod;
-
-        /**
-         * {@link InputMethodInfo} of the currently selected multi-client IME.  This must be
-         * non-{@code null} unless {@link #mState} is {@link PerUserState#SERVICE_NOT_QUERIED}.
-         */
-        @GuardedBy("mLock")
-        @Nullable
-        InputMethodInfo mCurrentInputMethodInfo;
-
-        /**
-         * Describes the current service state.
-         */
-        @GuardedBy("mLock")
-        @PerUserState
-        int mState;
-
-        /**
-         * A {@link SparseArray} that maps display ID to IME Window token that is already issued to
-         * the IME.
-         */
-        @GuardedBy("mLock")
-        final ArraySet<TokenInfo> mDisplayIdToImeWindowTokenMap = new ArraySet<>();
-
-        @GuardedBy("mLock")
-        private final ArrayMap<IBinder, InputMethodClientInfo> mClientMap = new ArrayMap<>();
-
-        @GuardedBy("mLock")
-        private SparseArray<InputMethodClientInfo> mClientIdToClientMap = new SparseArray<>();
-
-        private final OnWorkerThreadServiceConnection mOnWorkerThreadServiceConnection;
-
-        /**
-         * A {@link ServiceConnection} that is designed to run on a certain worker thread with
-         * which {@link OnWorkerThreadCallback} is associated.
-         *
-         * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle).
-         */
-        private static final class OnWorkerThreadServiceConnection implements ServiceConnection {
-            private final PerUserData mData;
-            private final OnWorkerThreadCallback mCallback;
-
-            OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback) {
-                mData = data;
-                mCallback = callback;
-            }
-
-            @WorkerThread
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                mCallback.onServiceConnected(mData,
-                        IMultiClientInputMethod.Stub.asInterface(service));
-            }
-
-            @WorkerThread
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                mCallback.onServiceDisconnected(mData);
-            }
-
-            @WorkerThread
-            @Override
-            public void onBindingDied(ComponentName name) {
-                mCallback.onBindingDied(mData);
-            }
-
-            Handler getHandler() {
-                return mCallback.getHandler();
-            }
-        }
-
-        PerUserData(@UserIdInt int userId, @Nullable InputMethodInfo inputMethodInfo,
-                @PerUserState int initialState, OnWorkerThreadCallback callback) {
-            mUserId = userId;
-            mCurrentInputMethodInfo = inputMethodInfo;
-            mState = initialState;
-            mOnWorkerThreadServiceConnection =
-                    new OnWorkerThreadServiceConnection(this, callback);
-        }
-
-        @GuardedBy("mLock")
-        boolean bindServiceLocked(Context context, @UserIdInt int userId) {
-            final Intent intent =
-                    new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
-                            .setComponent(mCurrentInputMethodInfo.getComponent())
-                            .putExtra(Intent.EXTRA_CLIENT_LABEL,
-                                    com.android.internal.R.string.input_method_binding_label)
-                            .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
-                                    context, 0,
-                                    new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
-                                    PendingIntent.FLAG_MUTABLE));
-
-            // Note: Instead of re-dispatching callback from the main thread to the worker thread
-            // where OnWorkerThreadCallback is running, we pass the Handler object here so that
-            // the callbacks will be directly dispatched to the worker thread.
-            return context.bindServiceAsUser(intent, mOnWorkerThreadServiceConnection,
-                    IME_CONNECTION_UNIFIED_BIND_FLAGS,
-                    mOnWorkerThreadServiceConnection.getHandler(), UserHandle.of(userId));
-        }
-
-        @GuardedBy("mLock")
-        void unbindServiceLocked(Context context) {
-            context.unbindService(mOnWorkerThreadServiceConnection);
-        }
-
-        @GuardedBy("mLock")
-        @Nullable
-        InputMethodClientInfo getClientLocked(IInputMethodClient client) {
-            return mClientMap.get(client.asBinder());
-        }
-
-        @GuardedBy("mLock")
-        @Nullable
-        InputMethodClientInfo getClientFromIdLocked(int clientId) {
-            return mClientIdToClientMap.get(clientId);
-        }
-
-        @GuardedBy("mLock")
-        @Nullable
-        InputMethodClientInfo removeClientLocked(IInputMethodClient client) {
-            final InputMethodClientInfo info = mClientMap.remove(client.asBinder());
-            if (info != null) {
-                mClientIdToClientMap.remove(info.mClientId);
-            }
-            return info;
-        }
-
-        @GuardedBy("mLock")
-        void addClientLocked(int uid, int pid, IInputMethodClient client,
-                int selfReportedDisplayId) {
-            if (getClientLocked(client) != null) {
-                Slog.wtf(TAG, "The same client is added multiple times");
-                return;
-            }
-            final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
-            try {
-                client.asBinder().linkToDeath(deathRecipient, 0);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
-            }
-            final InputMethodClientInfo clientInfo =
-                    new InputMethodClientInfo(client, uid, pid, selfReportedDisplayId);
-            clientInfo.mState = InputMethodClientState.REGISTERED;
-            mClientMap.put(client.asBinder(), clientInfo);
-            mClientIdToClientMap.put(clientInfo.mClientId, clientInfo);
-            switch (mState) {
-                case PerUserState.SERVICE_CONNECTED:
-                    try {
-                        mCurrentInputMethod.addClient(
-                                clientInfo.mClientId, clientInfo.mPid, clientInfo.mUid,
-                                clientInfo.mSelfReportedDisplayId);
-                        clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
-                    } catch (RemoteException e) {
-                        // TODO(yukawa): Need logging and expected behavior
-                    }
-                    break;
-            }
-        }
-
-        @GuardedBy("mLock")
-        void onInputMethodConnectedLocked() {
-            final int numClients = mClientMap.size();
-            for (int i = 0; i < numClients; ++i) {
-                final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.REGISTERED:
-                        // OK
-                        break;
-                    default:
-                        Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
-                        return;
-                }
-                try {
-                    mCurrentInputMethod.addClient(
-                            clientInfo.mClientId, clientInfo.mUid, clientInfo.mPid,
-                            clientInfo.mSelfReportedDisplayId);
-                    clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
-                } catch (RemoteException e) {
-                }
-            }
-        }
-
-        @GuardedBy("mLock")
-        void onInputMethodDisconnectedLocked() {
-            final int numClients = mClientMap.size();
-            for (int i = 0; i < numClients; ++i) {
-                final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.REGISTERED:
-                        // Disconnected before onInputMethodConnectedLocked().
-                        break;
-                    case InputMethodClientState.WAITING_FOR_IME_SESSION:
-                        // Disconnected between addClient() and acceptClient().
-                        clientInfo.mState = InputMethodClientState.REGISTERED;
-                        break;
-                    case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
-                        clientInfo.mState = InputMethodClientState.REGISTERED;
-                        clientInfo.mInputMethodSession = null;
-                        clientInfo.mMSInputMethodSession = null;
-                        if (clientInfo.mWriteChannel != null) {
-                            clientInfo.mWriteChannel.dispose();
-                            clientInfo.mWriteChannel = null;
-                        }
-                        break;
-                    case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
-                        try {
-                            clientInfo.mClient.onUnbindMethod(clientInfo.mBindingSequence,
-                                    UnbindReason.DISCONNECT_IME);
-                        } catch (RemoteException e) {
-                        }
-                        clientInfo.mState = InputMethodClientState.REGISTERED;
-                        clientInfo.mInputMethodSession = null;
-                        clientInfo.mMSInputMethodSession = null;
-                        if (clientInfo.mWriteChannel != null) {
-                            clientInfo.mWriteChannel.dispose();
-                            clientInfo.mWriteChannel = null;
-                        }
-                        break;
-                }
-            }
-        }
-
-        @AnyThread
-        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
-            synchronized (mLock) {
-                ipw.println("mState=" + mState
-                        + ",mCurrentInputMethod=" + mCurrentInputMethod
-                        + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo);
-
-                if (mCurrentInputMethod != null) {
-                    // indentation will not be kept. So add visual separator here.
-                    ipw.println(">>Dump CurrentInputMethod>>");
-                    ipw.flush();
-                    try {
-                        TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args);
-                    } catch (IOException | RemoteException e) {
-                        ipw.println("Failed to dump input method service: " + e);
-                    }
-                    ipw.println("<<Dump CurrentInputMethod<<");
-                }
-
-                ipw.println("mDisplayIdToImeWindowTokenMap=");
-                for (TokenInfo info : mDisplayIdToImeWindowTokenMap) {
-                    ipw.println(" display=" + info.mDisplayId + ",token="
-                            + info.mToken);
-                }
-                ipw.println("mClientMap=");
-                ipw.increaseIndent();
-                for (int i = 0; i < mClientMap.size(); i++) {
-
-                    ipw.println("binder=" + mClientMap.keyAt(i));
-                    ipw.println(" InputMethodClientInfo=");
-                    InputMethodClientInfo info = mClientMap.valueAt(i);
-                    if (info != null) {
-                        ipw.increaseIndent();
-                        info.dumpLocked(fd, ipw, args);
-                        ipw.decreaseIndent();
-                    }
-                }
-                ipw.decreaseIndent();
-                ipw.println("mClientIdToClientMap=");
-                ipw.increaseIndent();
-                for (int i = 0; i < mClientIdToClientMap.size(); i++) {
-                    ipw.println("clientId=" + mClientIdToClientMap.keyAt(i));
-                    ipw.println(" InputMethodClientInfo=");
-                    InputMethodClientInfo info = mClientIdToClientMap.valueAt(i);
-                    if (info != null) {
-                        ipw.increaseIndent();
-                        info.dumpLocked(fd, ipw, args);
-                        ipw.decreaseIndent();
-                    }
-                    if (info.mClient != null) {
-                        // indentation will not be kept. So add visual separator here.
-                        ipw.println(">>DumpClientStart>>");
-                        ipw.flush(); // all writes should be flushed to guarantee order.
-                        try {
-                            TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args);
-                        } catch (IOException | RemoteException e) {
-                            ipw.println(" Failed to dump client:" + e);
-                        }
-                        ipw.println("<<DumpClientEnd<<");
-                    }
-                }
-                ipw.decreaseIndent();
-            }
-        }
-
-        private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
-            private final PerUserData mPerUserData;
-            private final IInputMethodClient mClient;
-
-            ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client) {
-                mPerUserData = perUserData;
-                mClient = client;
-            }
-
-            @BinderThread
-            @Override
-            public void binderDied() {
-                synchronized (mPerUserData.mLock) {
-                    mClient.asBinder().unlinkToDeath(this, 0);
-
-                    final InputMethodClientInfo clientInfo =
-                            mPerUserData.removeClientLocked(mClient);
-                    if (clientInfo == null) {
-                        return;
-                    }
-
-                    if (clientInfo.mWriteChannel != null) {
-                        clientInfo.mWriteChannel.dispose();
-                        clientInfo.mWriteChannel = null;
-                    }
-                    if (clientInfo.mInputMethodSession != null) {
-                        try {
-                            clientInfo.mInputMethodSession.finishSession();
-                        } catch (RemoteException e) {
-                        }
-                        clientInfo.mInputMethodSession = null;
-                    }
-                    clientInfo.mMSInputMethodSession = null;
-                    clientInfo.mState = InputMethodClientState.UNREGISTERED;
-                    switch (mPerUserData.mState) {
-                        case PerUserState.SERVICE_CONNECTED:
-                            try {
-                                mPerUserData.mCurrentInputMethod.removeClient(clientInfo.mClientId);
-                            } catch (RemoteException e) {
-                                // TODO(yukawa): Need logging and expected behavior
-                            }
-                            break;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Queries for multi-client IME specified with {@code componentName}.
-     *
-     * @param context {@link Context} to be used to query component.
-     * @param userId User ID for which the multi-client IME is queried.
-     * @param componentName {@link ComponentName} to be queried.
-     * @return {@link InputMethodInfo} when multi-client IME is found. Otherwise {@code null}.
-     */
-    @Nullable
-    private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
-            @Nullable ComponentName componentName) {
-        if (componentName == null) {
-            Slog.w(TAG, "queryInputMethod invoked with null componentName");
-            return null;
-        }
-
-        // Use for queryIntentServicesAsUser
-        final PackageManager pm = context.getPackageManager();
-        final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
-                new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
-                        .setComponent(componentName),
-                PackageManager.GET_META_DATA, userId);
-        try {
-            return new InputMethodInfo(context, resolveMultiClientImeService(services));
-        } catch (Exception e) {
-            Slog.wtf(TAG, "Unable to load input method from services (" + services + ")", e);
-        }
-        return null;
-    }
-
-    /**
-     * Determines the multi-client IME from the specified {@link List<ResolveInfo>}.
-     *
-     * @return {@link ResolveInfo} when an appropriate multi-client IME is found.
-     *         Otherwise {@code null}.
-     */
-    @Nullable
-    @VisibleForTesting
-    static ResolveInfo resolveMultiClientImeService(@NonNull List<ResolveInfo> services) {
-        if (services.isEmpty()) {
-            Slog.e(TAG, "No IME found");
-            return null;
-        }
-        if (services.size() > 1) {
-            Slog.e(TAG, "Only one IME service is supported.");
-            return null;
-        }
-        final ResolveInfo ri = services.get(0);
-        ServiceInfo si = ri.serviceInfo;
-        final String imeId = InputMethodInfo.computeId(ri);
-        if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
-            Slog.e(TAG, imeId + " must have required"
-                    + android.Manifest.permission.BIND_INPUT_METHOD);
-            return null;
-        }
-        if (!Build.IS_DEBUGGABLE && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
-            return null;
-        }
-        return ri;
-    }
-
-    /**
-     * Manages the mapping rule from user ID to {@link InputMethodInfo}.
-     */
-    private static final class UserToInputMethodInfoMap {
-        @GuardedBy("mArray")
-        private final SparseArray<InputMethodInfo> mArray = new SparseArray<>();
-
-        @AnyThread
-        void put(@UserIdInt int userId, InputMethodInfo imi) {
-            synchronized (mArray) {
-                mArray.put(userId, imi);
-            }
-        }
-
-        @AnyThread
-        void remove(@UserIdInt int userId) {
-            synchronized (mArray) {
-                mArray.remove(userId);
-            }
-        }
-
-        @AnyThread
-        @Nullable
-        InputMethodInfo get(@UserIdInt int userId) {
-            synchronized (mArray) {
-                return mArray.get(userId);
-            }
-        }
-
-        @AnyThread
-        List<InputMethodInfo> getAsList(@UserIdInt int userId) {
-            final InputMethodInfo info = get(userId);
-            if (info == null) {
-                return Collections.emptyList();
-            }
-            return Collections.singletonList(info);
-        }
-
-        @AnyThread
-        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
-            synchronized (mArray) {
-                for (int i = 0; i < mArray.size(); i++) {
-                    ipw.println("userId=" + mArray.keyAt(i));
-                    ipw.println(" InputMethodInfo=" + mArray.valueAt(i));
-                }
-            }
-        }
-    }
-
-    /**
-     * Takes care of IPCs exposed to the multi-client IME.
-     */
-    private static final class ImeCallbacks
-            extends IMultiClientInputMethodPrivilegedOperations.Stub {
-        private final PerUserData mPerUserData;
-        private final WindowManagerInternal mIWindowManagerInternal;
-
-        ImeCallbacks(PerUserData perUserData) {
-            mPerUserData = perUserData;
-            mIWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
-        }
-
-        @BinderThread
-        @Override
-        public IBinder createInputMethodWindowToken(int displayId) {
-            synchronized (mPerUserData.mLock) {
-                // We assume the number of tokens would not be that large (up to 10 or so) hence
-                // linear search should be acceptable.
-                final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
-                for (int i = 0; i < numTokens; ++i) {
-                    final TokenInfo tokenInfo =
-                            mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
-                    // Currently we issue up to one window token per display.
-                    if (tokenInfo.mDisplayId == displayId) {
-                        return tokenInfo.mToken;
-                    }
-                }
-
-                final Binder token = new Binder();
-                Binder.withCleanCallingIdentity(
-                        PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
-                                mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
-                                null /* options */));
-                mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
-                return token;
-            }
-        }
-
-        @BinderThread
-        @Override
-        public void deleteInputMethodWindowToken(IBinder token) {
-            synchronized (mPerUserData.mLock) {
-                // We assume the number of tokens would not be that large (up to 10 or so) hence
-                // linear search should be acceptable.
-                final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
-                for (int i = 0; i < numTokens; ++i) {
-                    final TokenInfo tokenInfo =
-                            mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
-                    if (tokenInfo.mToken == token) {
-                        mPerUserData.mDisplayIdToImeWindowTokenMap.remove(tokenInfo);
-                        break;
-                    }
-                }
-            }
-        }
-
-        @BinderThread
-        @Override
-        public void acceptClient(int clientId, IInputMethodSession inputMethodSession,
-                IMultiClientInputMethodSession multiSessionInputMethodSession,
-                InputChannel writeChannel) {
-            synchronized (mPerUserData.mLock) {
-                final InputMethodClientInfo clientInfo =
-                        mPerUserData.getClientFromIdLocked(clientId);
-                if (clientInfo == null) {
-                    Slog.e(TAG, "Unknown clientId=" + clientId);
-                    return;
-                }
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.WAITING_FOR_IME_SESSION:
-                        try {
-                            clientInfo.mClient.setActive(true, false, false);
-                        } catch (RemoteException e) {
-                            // TODO(yukawa): Remove this client.
-                            return;
-                        }
-                        clientInfo.mState = InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT;
-                        clientInfo.mWriteChannel = writeChannel;
-                        clientInfo.mInputMethodSession = inputMethodSession;
-                        clientInfo.mMSInputMethodSession = multiSessionInputMethodSession;
-                        break;
-                    default:
-                        Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
-                        break;
-                }
-            }
-        }
-
-        @BinderThread
-        @Override
-        public void reportImeWindowTarget(int clientId, int targetWindowHandle,
-                IBinder imeWindowToken) {
-            synchronized (mPerUserData.mLock) {
-                final InputMethodClientInfo clientInfo =
-                        mPerUserData.getClientFromIdLocked(clientId);
-                if (clientInfo == null) {
-                    Slog.e(TAG, "Unknown clientId=" + clientId);
-                    return;
-                }
-                for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) {
-                    if (windowInfo.mWindowHandle == targetWindowHandle) {
-                        final IBinder targetWindowToken = windowInfo.mWindowToken;
-                        if (DEBUG) {
-                            Slog.v(TAG, "reportImeWindowTarget"
-                                    + " clientId=" + clientId
-                                    + " imeWindowToken=" + imeWindowToken
-                                    + " targetWindowToken=" + targetWindowToken);
-                        }
-                        mIWindowManagerInternal.updateInputMethodTargetWindow(
-                                imeWindowToken, targetWindowToken);
-                    }
-                }
-                // not found.
-            }
-        }
-
-        @BinderThread
-        @Override
-        public boolean isUidAllowedOnDisplay(int displayId, int uid) {
-            return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid);
-        }
-
-        @BinderThread
-        @Override
-        public void setActive(int clientId, boolean active) {
-            synchronized (mPerUserData.mLock) {
-                final InputMethodClientInfo clientInfo =
-                        mPerUserData.getClientFromIdLocked(clientId);
-                if (clientInfo == null) {
-                    Slog.e(TAG, "Unknown clientId=" + clientId);
-                    return;
-                }
-                try {
-                    clientInfo.mClient.setActive(active, false /* fullscreen */, false);
-                } catch (RemoteException e) {
-                    return;
-                }
-            }
-        }
-    }
-
-    /**
-     * Takes care of IPCs exposed to the IME client.
-     */
-    private static final class ApiCallbacks extends IInputMethodManager.Stub {
-        private final Context mContext;
-        private final UserDataMap mUserDataMap;
-        private final UserToInputMethodInfoMap mInputMethodInfoMap;
-        private final AppOpsManager mAppOpsManager;
-        private final WindowManagerInternal mWindowManagerInternal;
-
-        ApiCallbacks(Context context, UserDataMap userDataMap,
-                UserToInputMethodInfoMap inputMethodInfoMap) {
-            mContext = context;
-            mUserDataMap = userDataMap;
-            mInputMethodInfoMap = inputMethodInfoMap;
-            mAppOpsManager = context.getSystemService(AppOpsManager.class);
-            mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
-        }
-
-        @AnyThread
-        private boolean checkFocus(int uid, int pid, int displayId) {
-            return mWindowManagerInternal.isInputMethodClientFocus(uid, pid, displayId);
-        }
-
-        @BinderThread
-        @Override
-        public void addClient(IInputMethodClient client, IInputContext inputContext,
-                int selfReportedDisplayId) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int userId = UserHandle.getUserId(callingUid);
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.e(TAG, "addClient() from unknown userId=" + userId
-                        + " uid=" + callingUid + " pid=" + callingPid);
-                return;
-            }
-            synchronized (data.mLock) {
-                data.addClientLocked(callingUid, callingPid, client, selfReportedDisplayId);
-            }
-        }
-
-        @BinderThread
-        @Override
-        public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
-            if (UserHandle.getCallingUserId() != userId) {
-                mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
-            }
-            return mInputMethodInfoMap.getAsList(userId);
-        }
-
-        @BinderThread
-        @Override
-        public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
-            if (UserHandle.getCallingUserId() != userId) {
-                mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
-            }
-            return mInputMethodInfoMap.getAsList(userId);
-        }
-
-        @BinderThread
-        @Override
-        public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
-                boolean allowsImplicitlySelectedSubtypes) {
-            reportNotSupported();
-            return Collections.emptyList();
-        }
-
-        @BinderThread
-        @Override
-        public InputMethodSubtype getLastInputMethodSubtype() {
-            reportNotSupported();
-            return null;
-        }
-
-        @BinderThread
-        @Override
-        public void removeImeSurface() {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public boolean showSoftInput(
-                IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver,
-                @SoftInputShowHideReason int reason) {
-            return showSoftInputInternal(client, token, flags, resultReceiver);
-        }
-
-        @BinderThread
-        private boolean showSoftInputInternal(
-                IInputMethodClient client, IBinder token, int flags,
-                ResultReceiver resultReceiver) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int userId = UserHandle.getUserId(callingUid);
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.e(TAG, "showSoftInput() from unknown userId=" + userId
-                        + " uid=" + callingUid + " pid=" + callingPid);
-                return false;
-            }
-            synchronized (data.mLock) {
-                final InputMethodClientInfo clientInfo = data.getClientLocked(client);
-                if (clientInfo == null) {
-                    Slog.e(TAG, "showSoftInput. client not found. ignoring.");
-                    return false;
-                }
-                if (clientInfo.mUid != callingUid) {
-                    Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
-                            + " actual=" + callingUid);
-                    return false;
-                }
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
-                    case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
-                        try {
-                            clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver);
-
-                            // Forcing WM to show IME on imeTargetWindow
-                            mWindowManagerInternal.showImePostLayout(token);
-                        } catch (RemoteException e) {
-                        }
-                        break;
-                    default:
-                        if (DEBUG) {
-                            Slog.e(TAG, "Ignoring showSoftInput(). clientState="
-                                    + clientInfo.mState);
-                        }
-                        break;
-                }
-                return true;
-            }
-        }
-
-        @BinderThread
-        @Override
-        public boolean hideSoftInput(
-                IInputMethodClient client, IBinder windowToken, int flags,
-                ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
-            return hideSoftInputInternal(client, windowToken, flags, resultReceiver);
-        }
-
-        @BinderThread
-        private boolean hideSoftInputInternal(
-                IInputMethodClient client, IBinder windowToken, int flags,
-                ResultReceiver resultReceiver) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int userId = UserHandle.getUserId(callingUid);
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.e(TAG, "hideSoftInput() from unknown userId=" + userId
-                        + " uid=" + callingUid + " pid=" + callingPid);
-                return false;
-            }
-            synchronized (data.mLock) {
-                final InputMethodClientInfo clientInfo = data.getClientLocked(client);
-                if (clientInfo == null) {
-                    return false;
-                }
-                if (clientInfo.mUid != callingUid) {
-                    Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
-                            + " actual=" + callingUid);
-                    return false;
-                }
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
-                    case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
-                        try {
-                            clientInfo.mMSInputMethodSession.hideSoftInput(flags, resultReceiver);
-                        } catch (RemoteException e) {
-                        }
-                        break;
-                    default:
-                        if (DEBUG) {
-                            Slog.e(TAG, "Ignoring hideSoftInput(). clientState="
-                                    + clientInfo.mState);
-                        }
-                        break;
-                }
-                return true;
-            }
-        }
-
-        @BinderThread
-        @Override
-        public InputBindResult startInputOrWindowGainedFocus(
-                @StartInputReason int startInputReason,
-                @Nullable IInputMethodClient client,
-                @Nullable IBinder windowToken,
-                @StartInputFlags int startInputFlags,
-                @SoftInputModeFlags int softInputMode,
-                int windowFlags,
-                @Nullable EditorInfo editorInfo,
-                @Nullable IInputContext inputContext,
-                @MissingMethodFlags int missingMethods,
-                int unverifiedTargetSdkVersion) {
-            return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
-                    startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
-                    missingMethods, unverifiedTargetSdkVersion);
-        }
-
-        @BinderThread
-        private InputBindResult startInputOrWindowGainedFocusInternal(
-                @StartInputReason int startInputReason,
-                @Nullable IInputMethodClient client,
-                @Nullable IBinder windowToken,
-                @StartInputFlags int startInputFlags,
-                @SoftInputModeFlags int softInputMode,
-                int windowFlags,
-                @Nullable EditorInfo editorInfo,
-                @Nullable IInputContext inputContext,
-                @MissingMethodFlags int missingMethods,
-                int unverifiedTargetSdkVersion) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int userId = UserHandle.getUserId(callingUid);
-
-            if (client == null) {
-                return InputBindResult.INVALID_CLIENT;
-            }
-
-            final boolean packageNameVerified =
-                    editorInfo != null && InputMethodUtils.checkIfPackageBelongsToUid(
-                            mAppOpsManager, callingUid, editorInfo.packageName);
-            if (editorInfo != null && !packageNameVerified) {
-                Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
-                        + " uid=" + callingUid + " package=" + editorInfo.packageName);
-                return InputBindResult.INVALID_PACKAGE_NAME;
-            }
-
-            final PerUserData data = mUserDataMap.get(userId);
-            if (data == null) {
-                Slog.e(TAG, "startInputOrWindowGainedFocus() from unknown userId=" + userId
-                        + " uid=" + callingUid + " pid=" + callingPid);
-                return InputBindResult.INVALID_USER;
-            }
-
-            synchronized (data.mLock) {
-                final InputMethodClientInfo clientInfo = data.getClientLocked(client);
-                if (clientInfo == null) {
-                    return InputBindResult.INVALID_CLIENT;
-                }
-                if (clientInfo.mUid != callingUid) {
-                    Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
-                            + " actual=" + callingUid);
-                    return InputBindResult.INVALID_CLIENT;
-                }
-
-                switch (data.mState) {
-                    case PerUserState.USER_LOCKED:
-                    case PerUserState.SERVICE_NOT_QUERIED:
-                    case PerUserState.SERVICE_RECOGNIZED:
-                    case PerUserState.WAITING_SERVICE_CONNECTED:
-                    case PerUserState.UNBIND_CALLED:
-                        return InputBindResult.IME_NOT_CONNECTED;
-                    case PerUserState.SERVICE_CONNECTED:
-                        // OK
-                        break;
-                    default:
-                        Slog.wtf(TAG, "Unexpected state=" + data.mState);
-                        return InputBindResult.IME_NOT_CONNECTED;
-                }
-
-                WindowInfo windowInfo = null;
-                if (windowToken != null) {
-                    windowInfo = clientInfo.mWindowMap.get(windowToken);
-                    if (windowInfo == null) {
-                        windowInfo = new WindowInfo(windowToken, WindowHandleSource.getNext());
-                        clientInfo.mWindowMap.put(windowToken, windowInfo);
-                    }
-                }
-
-                if (!checkFocus(clientInfo.mUid, clientInfo.mPid,
-                        clientInfo.mSelfReportedDisplayId)) {
-                    return InputBindResult.NOT_IME_TARGET_WINDOW;
-                }
-
-                if (editorInfo == null) {
-                    // So-called fallback InputConnection scenario.  For app compatibility, we still
-                    // notify this to the IME.
-                    switch (clientInfo.mState) {
-                        case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
-                        case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
-                            final int windowHandle = windowInfo != null
-                                    ? windowInfo.mWindowHandle
-                                    : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
-                            try {
-                                clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
-                                        inputContext, missingMethods, editorInfo, startInputFlags,
-                                        softInputMode, windowHandle);
-                            } catch (RemoteException ignored) { }
-                            break;
-                    }
-                    return InputBindResult.NULL_EDITOR_INFO;
-                }
-
-                switch (clientInfo.mState) {
-                    case InputMethodClientState.REGISTERED:
-                    case InputMethodClientState.WAITING_FOR_IME_SESSION:
-                        clientInfo.mBindingSequence++;
-                        if (clientInfo.mBindingSequence < 0) {
-                            clientInfo.mBindingSequence = 0;
-                        }
-                        return new InputBindResult(
-                                InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                                null, null, data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence, false);
-                    case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
-                    case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
-                        clientInfo.mBindingSequence++;
-                        if (clientInfo.mBindingSequence < 0) {
-                            clientInfo.mBindingSequence = 0;
-                        }
-                        // Successful start input.
-                        final int windowHandle = windowInfo != null
-                                ? windowInfo.mWindowHandle
-                                : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
-                        try {
-                            clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
-                                    inputContext, missingMethods, editorInfo, startInputFlags,
-                                    softInputMode, windowHandle);
-                        } catch (RemoteException ignored) { }
-                        clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
-                        return new InputBindResult(
-                                InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
-                                clientInfo.mInputMethodSession,
-                                clientInfo.mWriteChannel.dup(),
-                                data.mCurrentInputMethodInfo.getId(),
-                                clientInfo.mBindingSequence, false);
-                    case InputMethodClientState.UNREGISTERED:
-                        Slog.e(TAG, "The client is already unregistered.");
-                        return InputBindResult.INVALID_CLIENT;
-                }
-            }
-            return null;
-        }
-
-        @BinderThread
-        @Override
-        public void showInputMethodPickerFromClient(IInputMethodClient client,
-                int auxiliarySubtypeMode) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public void showInputMethodPickerFromSystem(IInputMethodClient client,
-                int auxiliarySubtypeMode, int displayId) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client,
-                String inputMethodId) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public boolean isInputMethodPickerShownForTest() {
-            reportNotSupported();
-            return false;
-        }
-
-        @BinderThread
-        @Override
-        public InputMethodSubtype getCurrentInputMethodSubtype() {
-            reportNotSupported();
-            return null;
-        }
-
-        @BinderThread
-        @Override
-        public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public int getInputMethodWindowVisibleHeight() {
-            reportNotSupported();
-            return 0;
-        }
-
-        @BinderThread
-        @Override
-        public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) {
-            reportNotSupported();
-        }
-
-        @BinderThread
-        @Override
-        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
-                @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
-                ResultReceiver resultReceiver) {
-        }
-
-        @BinderThread
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-            final String prefixChild = "  ";
-            pw.println("Current Multi Client Input Method Manager state:");
-            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-            ipw.println("mUserDataMap=");
-            if (mUserDataMap != null) {
-                ipw.increaseIndent();
-                mUserDataMap.dump(fd, ipw, args);
-            }
-        }
-
-        @BinderThread
-        @Override
-        public void startProtoDump(byte[] clientProtoDump, int source, String where) {
-        }
-
-        @BinderThread
-        @Override
-        public boolean isImeTraceEnabled() {
-            return false;
-        }
-
-        @BinderThread
-        @Override
-        public void startImeTrace() {
-        }
-
-        @BinderThread
-        @Override
-        public void stopImeTrace() {
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/inputmethod/multi-client-ime.md b/services/core/java/com/android/server/inputmethod/multi-client-ime.md
deleted file mode 100644
index ba92cd2..0000000
--- a/services/core/java/com/android/server/inputmethod/multi-client-ime.md
+++ /dev/null
@@ -1,194 +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.
--->
-
-# Multi Client Input Method Editors
-
-## History of Multi Client Input Method Editors (Multi Client IMEs)
-
-An advanced multi-display support is requested for certain Android form-factors so that user(s) can type text on each display at the same time without losing software keyboard focus in other displays (hereafter called "multi-client scenario"). This is not possible in Android IMEs built on top of `InputMethodService` class. The assumption that a single IME client can be focused at the same time was made before Android IME APIs were introduced in Android 1.5 and many public APIs in `InputMethodService` have already relied heavily on that assumption (hereafter called "single-client scenario"). Updating `InputMethodService` class to support multi-client scenario is, however, quite challenging because:
-
- 1. doing so would introduce an unacceptable amount of complexity into `InputMethodService`, which is already hard to maintain,
- 2. IME developers still need to update their implementation to be able to support parallel requests from multiple focused IME client, which may require non-trivial redesign in their side (e.g. input decoder, typing history database, ...), and
- 3. actual use cases for multi IME clients are expected to be evolved rapidly hence the new protocol is not yet stable and not yet ready to be exposed as public APIs.
-
-Thus the first decision we made was that to support such special multi-display environments a new type of IME (hereafter called "multi-client IME") needs to be designed and implemented rather than reusing `InputMethodService` public class. On top of this decision, following decisions were also made:
-
- * Multi-client IME V1 will be built on top of private APIs. This means:
-   * Multi-client IME must be pre-installed into the system. They cannot be distributed via application store since protocol compatibility is not guaranteed across devices and releases.
-   * The system should trust multi-client IME to some extent. System integrators are responsible for making sure that the pre-installed multi-client IME works as expected.
- * Unlike `InputMethodService`, multiple multi-client IMEs cannot be enabled. The system pre-installs only one multi-client IME.
- * Punt some special features of Android IMEs (e.g. fullscreen mode, InputMethodSubtype, ...) from V1 goal unless someone actually requests those features for multi-client IME scenario.
- * Introduce `MultiClientInputMethodManagerService` (MCIMMS) for multi-client IME scenario and use it instead of `InputMethodManagerService` (IMMS) when a certain runtime flag is enabled at the device boot time. This means:
-   * basically no risk for single-client scenario,
-   * the feature can be easily deprecated, and
-   * it forces us to rewrite IME system server, which is expected to be a good chance to reconsider what Android IME protocol should look like.
- * Most of form-factors such as Phones and TVs continue to use IMMS and support at most one focused IME client even under multi-display environment.
-
-
-## How to test
-
-For multi-client IME to properly work, an internal boolean resource `com.android.internal.R.bool.config_perDisplayFocusEnabled` needs to be `true`. Since this value cannot be overridden at the run time, you may need to rebuild the system image to enable per-display focus mode.
-
-As for multi-client IME mode itself, you can enable multi-client IME mode just by setting a valid component name that supports multi-client IME protocol to the system property `persist.debug.multi_client_ime`, as long as `android.os.Build.IS_DEBUGGABLE` returns `true` and you can have root access. Reboot is required for this to take effect.
-
-```shell
-# Build and install a sample multi-client IME
-make -j MultiClientInputMethod
-adb install -r $OUT/system/priv-app/MultiClientInputMethod/MultiClientInputMethod.apk
-
-# Enable multi-client IME for the side-loaded sample multi-client IME
-adb root
-adb shell setprop persist.debug.multi_client_ime com.example.android.multiclientinputmethod/.MultiClientInputMethod
-adb reboot
-```
-
-To disable multi-client IME on non-supported devices again, just clear `persist.debug.multi_client_ime` as follows. Reboot is still required for this to take effect.
-
-```shell
-# Disable multi-client IME again
-adb root
-adb shell "setprop persist.debug.multi_client_ime ''"
-adb reboot
-```
-
-## How to develop multi-client IMEs
-
-There is a sample multi-client IME in `development/samples/MultiClientInputMethod/`.
-
-## Versioning
-
-Neither forward nor backward compatibility is guaranteed in multi-client IME APIs. The system integrator is responsible for making sure that both the system and pre-installed multi-client IME are compatible with each other every time the system image is updated.
-
-## Implementation note
-
-### Unsupported features
-
- * VR IME
-   * `VrManager#setVrInputMethod()` system API is not supported.
- * InputMethodSubtype
-   * Following APIs are not supported
-     * `InputMethodManager#getEnabledInputMethodSubtypeList()`
-     * `InputMethodManager#getCurrentInputMethodSubtype()`
-     * `InputMethodManager#setCurrentInputMethodSubtype()`
-     * `InputMethodManager#getShortcutInputMethodsAndSubtypes()`
-     * `InputMethodManager#setAdditionalInputMethodSubtypes()`
-     * `InputMethodManager#getLastInputMethodSubtype()`
-     * `Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE`
- * IME switching
-   * Following APIs are not supported
-     * `InputMethodManager#showInputMethodPicker()`
-     * `InputMethodManager#showInputMethodAndSubtypeEnabler()`
-     * `InputMethodManager#setInputMethod()`
-     * `InputMethodManager#setInputMethodAndSubtype()`
-     * `InputMethodManager#switchToLastInputMethod()`
-     * `InputMethodManager#switchToNextInputMethod()`
-     * `InputMethodManager#shouldOfferSwitchingToNextInputMethod()`
-     * `Settings.Secure#DEFAULT_INPUT_METHOD`
-     * `Settings.Secure#ENABLED_INPUT_METHODS`
- * Direct-boot aware multi-client IME
-   * Device manufacturer can work around this by integrating in-app keyboard into the initial unlock screen.
- * Full-screen mode
-   * Following API always returns `false`.
-     * `InputMethodManager#isFullscreenMode()`
- * Custom inset
-   * For instance, floating IME cannot be implemented right now.
- * Custom touchable region (`InputMethodService.Insets#touchableRegion`)
- * Image Insertion API
-   * `InputConnection#commitContent()` API is silently ignored.
- * `adb shell dumpsys` does not include any log from MCIMMS yet.
-
-### Security
-
-#### Root permission is required to enable MCIMMS on non-supported devices
-
-In order to override `persist.debug.multi_client_ime` device property, an explicit root permission is needed.
-
-#### Multi-client IME must be pre-installed
-
-Multi-client IME must be pre-installed since it is considered as part of the system component. This is verified by checking `ApplicationInfo.FLAG_SYSTEM` bit. This security check can be bypassed when `Build.IS_DEBUGGABLE` is `true` so that IME developers can easily side-load their APKs during development phase.
-
-```java
-public final class MultiClientInputMethodManagerService {
-    ...
-    @Nullable
-    private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
-            @Nullable ComponentName componentName) {
-
-        ...
-
-        if (! && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
-            return null;
-        }
-```
-[services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java](MultiClientInputMethodManagerService.java)
-
-
-#### Integer handle vs IBinder token
-
-Sometimes MCIMMS needs to issue certain types of identifiers to the multi-client IME so that the IME can later specify to which entity or resource it intends to access. A good example is the IME client identifier. Multi-client IME definitely need to be able to specify which IME client to be interacted with for certain operations. The problem is that MCIMMS cannot simply pass `IInputMethodClient` to the multi-client IME as an ID because it would allow the IME to make IPC calls to the IME client. For this kind of situations, we usually use `Binder` object just as a non-spoofable token. For instance, IMMS creates another 'Binder' token then pass it to the IME, instead of directly passing 'IWindow' Binder token.
-
-```java
-public class InputMethodManagerService extends IInputMethodManager.Stub
-        implements ServiceConnection, Handler.Callback {
-    ...
-    @GuardedBy("mMethodMap")
-    private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
-
-    ...
-
-    @GuardedBy("mMethodMap")
-    @NonNull
-    InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
-        ...
-        final Binder startInputToken = new Binder();
-        final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
-                !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
-                mCurSeq);
-        mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
-        ...
-    }
-
-    ...
-
-    @BinderThread
-    private void reportStartInput(IBinder token, IBinder startInputToken) {
-        if (!calledWithValidToken(token)) {
-            return;
-        }
-
-        synchronized (mMethodMap) {
-            final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
-            if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
-                mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
-            }
-            mLastImeTargetWindow = targetWindow;
-        }
-    }
-```
-[services/core/java/com/android/server/inputmethod/InputMethodManagerService.java](InputMethodManagerService.java)
-
-However, in MCIMMS, for certain cases we decided to use a simple integer token, which can be spoofable and can be messed up if integer overflow happens. This is because:
-
- * It does not make much sense to worry about malicious multi-client IMEs, because it is guaranteed to be a pre-installed system component.
- * Integer token is expected to be a more lightweight that `Binder` token.
- * For that use case, integer overflow is unrealistic.
- * Strict user separation is still enforced. Multi-client IMEs are still not allowed to interact with other users' resources by any means.
-
-Currently the following IDs are implemented as integer tokens:
-
- * Client ID
- * Window Handle
-   * Note that each IME client has its own Window Handle mapping table. Window Handle is valid only within the associated IME client.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index b61c6a7..f2ccf9f 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -44,8 +44,11 @@
 import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -580,8 +583,14 @@
             int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
             // APK signatures is already verified elsewhere in PackageManager. We do not need to
             // verify it again since it could cause a timeout for large APKs.
-            pkg.setSigningDetails(
-                    ParsingPackageUtils.getSigningDetails(pkg, /* skipVerify= */ true));
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+                    input, pkg, /* skipVerify= */ true);
+            if (result.isError()) {
+                Slog.w(TAG, result.getErrorMessage(), result.getException());
+                return null;
+            }
+            pkg.setSigningDetails(result.getResult());
             return PackageInfoUtils.generate(
                     pkg,
                     null,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index e6210b2..7b5a635 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -771,14 +771,6 @@
         // sanitize request
         LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
 
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
-                && GPS_PROVIDER.equals(provider)
-                && ArrayUtils.contains(mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_locationDriverAssistancePackageNames),
-                identity.getPackageName())) {
-            sanitized.setAdasGnssBypass(true);
-        }
-
         if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
             if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
                     != PERMISSION_GRANTED) {
@@ -914,14 +906,6 @@
         // sanitize request
         LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
 
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
-                && GPS_PROVIDER.equals(provider)
-                && ArrayUtils.contains(mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_locationDriverAssistancePackageNames),
-                identity.getPackageName())) {
-            sanitized.setAdasGnssBypass(true);
-        }
-
         request = sanitized.build();
 
         // validate request
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index fa33338..3da5a43 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -210,11 +210,6 @@
     private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
 
     /*
-     * True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
-     */
-    private final boolean mHasAccessContextHubPermission;
-
-    /*
      * Map containing all nanoapps this client has a messaging channel with and whether it is
      * allowed to communicate over that channel. A channel is defined to have been opened if the
      * client has sent or received messages from the particular nanoapp.
@@ -327,8 +322,6 @@
 
         mPid = Binder.getCallingPid();
         mUid = Binder.getCallingUid();
-        mHasAccessContextHubPermission = context.checkCallingPermission(
-                Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
         startMonitoringOpChanges();
@@ -827,9 +820,7 @@
      */
     private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
         try {
-            String requiredPermission = mHasAccessContextHubPermission
-                    ? Manifest.permission.ACCESS_CONTEXT_HUB
-                    : Manifest.permission.LOCATION_HARDWARE;
+            String requiredPermission = Manifest.permission.ACCESS_CONTEXT_HUB;
             pendingIntent.send(
                     mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
                     requiredPermission, null /* options */);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 8361253..70f50c3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -30,28 +30,20 @@
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
-import android.os.Binder;
-import android.os.Build;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * A class encapsulating helper functions used by the ContextHubService class
  */
 /* package */ class ContextHubServiceUtil {
     private static final String TAG = "ContextHubServiceUtil";
-    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     private static final String CONTEXT_HUB_PERMISSION = Manifest.permission.ACCESS_CONTEXT_HUB;
 
-    // A set of packages that have already been warned regarding the ACCESS_CONTEXT_HUB permission.
-    private static final Set<String> PERMISSION_WARNED_PACKAGES = new HashSet<String>();
-
     /**
      * Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
      * ArrayList of HIDL ContextHub objects.
@@ -209,25 +201,12 @@
      */
     /* package */
     static void checkPermissions(Context context) {
-        boolean hasLocationHardwarePermission = (context.checkCallingPermission(HARDWARE_PERMISSION)
-                == PERMISSION_GRANTED);
         boolean hasAccessContextHubPermission = (context.checkCallingPermission(
                 CONTEXT_HUB_PERMISSION) == PERMISSION_GRANTED);
 
-        if (!hasLocationHardwarePermission && !hasAccessContextHubPermission) {
+        if (!hasAccessContextHubPermission) {
             throw new SecurityException(
-                    "LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permission required to use Context "
-                            + "Hub");
-        }
-
-        if (!hasAccessContextHubPermission && !Build.IS_USER) {
-            String pkgName = context.getPackageManager().getNameForUid(Binder.getCallingUid());
-            if (!PERMISSION_WARNED_PACKAGES.contains(pkgName)) {
-                Log.w(TAG, pkgName
-                        + ": please use the ACCESS_CONTEXT_HUB permission rather than "
-                        + "LOCATION_HARDWARE (will be removed for Context Hub APIs in T)");
-                PERMISSION_WARNED_PACKAGES.add(pkgName);
-            }
+                    "ACCESS_CONTEXT_HUB permission required to use Context Hub");
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 1967e02..e375007 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -82,7 +82,7 @@
     }
 
     public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             putRegistration(listener.asBinder(),
                     new AntennaInfoListenerRegistration(callerIdentity, listener));
@@ -92,7 +92,7 @@
     }
 
     public void removeListener(IGnssAntennaInfoListener listener) {
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             removeRegistration(listener.asBinder());
         } finally {
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 f3dcfbb..206682e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,8 +68,6 @@
 import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -138,13 +136,6 @@
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
-    // handler messages
-    private static final int INJECT_NTP_TIME = 5;
-    private static final int DOWNLOAD_PSDS_DATA = 6;
-    private static final int REQUEST_LOCATION = 16;
-    private static final int REPORT_LOCATION = 17; // HAL reports location
-    private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
-
     // TCP/IP constants.
     // Valid TCP/UDP port range is (0, 65535].
     private static final int TCP_MIN_PORT = 0;
@@ -402,7 +393,7 @@
                 BatteryStats.SERVICE_NAME));
 
         // Construct internal handler
-        mHandler = new ProviderHandler(FgThread.getHandler().getLooper());
+        mHandler = FgThread.getHandler();
 
         // Load GPS configuration and register listeners in the background:
         // some operations, such as opening files and registering broadcast receivers, can take a
@@ -530,7 +521,7 @@
         if (mSupportsPsds) {
             synchronized (mLock) {
                 for (int psdsType : mPendingDownloadPsdsTypes) {
-                    sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+                    postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
                 }
                 mPendingDownloadPsdsTypes.clear();
             }
@@ -654,9 +645,7 @@
                 synchronized (mLock) {
                     backoffMillis = mPsdsBackOff.nextBackoffMillis();
                 }
-                mHandler.sendMessageDelayed(
-                        mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null),
-                        backoffMillis);
+                mHandler.postDelayed(() -> handleDownloadPsdsData(psdsType), backoffMillis);
             }
 
             // Release wake lock held by task, synchronize on mLock in case multiple
@@ -976,8 +965,8 @@
             requestUtcTime();
         } else if ("force_psds_injection".equals(command)) {
             if (mSupportsPsds) {
-                sendMessage(DOWNLOAD_PSDS_DATA, GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX,
-                        null);
+                postWithWakeLockHeld(() -> handleDownloadPsdsData(
+                        GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
             }
         } else if ("request_power_stats".equals(command)) {
             mGnssNative.requestPowerStats();
@@ -1314,7 +1303,7 @@
 
     private void requestUtcTime() {
         if (DEBUG) Log.d(TAG, "utcTimeRequest");
-        sendMessage(INJECT_NTP_TIME, 0, null);
+        postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
     }
 
     private void requestRefLocation() {
@@ -1348,75 +1337,20 @@
         }
     }
 
-    boolean isInEmergencySession() {
-        return mNIHandler.getInEmergency();
-    }
-
-    private void sendMessage(int message, int arg, Object obj) {
+    private void postWithWakeLockHeld(Runnable runnable) {
         // hold a wake lock until this message is delivered
         // note that this assumes the message will not be removed from the queue before
         // it is handled (otherwise the wake lock would be leaked).
         mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
-        if (DEBUG) {
-            Log.d(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
-                    + ", " + obj + ")");
-        }
-        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
-    }
-
-    private final class ProviderHandler extends Handler {
-        ProviderHandler(Looper looper) {
-            super(looper, null, true /*async*/);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            int message = msg.what;
-            switch (message) {
-                case INJECT_NTP_TIME:
-                    mNtpTimeHelper.retrieveAndInjectNtpTime();
-                    break;
-                case REQUEST_LOCATION:
-                    handleRequestLocation(msg.arg1 == 1, (boolean) msg.obj);
-                    break;
-                case DOWNLOAD_PSDS_DATA:
-                    handleDownloadPsdsData(msg.arg1);
-                    break;
-                case REPORT_LOCATION:
-                    handleReportLocation(msg.arg1 == 1, (Location) msg.obj);
-                    break;
-                case REPORT_SV_STATUS:
-                    handleReportSvStatus((GnssStatus) msg.obj);
-                    break;
-            }
-            if (msg.arg2 == 1) {
-                // wakelock was taken for this message, release it
+        boolean success = mHandler.post(() -> {
+            try {
+                runnable.run();
+            } finally {
                 mWakeLock.release();
-                if (DEBUG) {
-                    Log.d(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
-                            + ", " + msg.arg1 + ", " + msg.obj + ")");
-                }
             }
-        }
-    }
-
-    /**
-     * @return A string representing the given message ID.
-     */
-    private String messageIdAsString(int message) {
-        switch (message) {
-            case INJECT_NTP_TIME:
-                return "INJECT_NTP_TIME";
-            case REQUEST_LOCATION:
-                return "REQUEST_LOCATION";
-            case DOWNLOAD_PSDS_DATA:
-                return "DOWNLOAD_PSDS_DATA";
-            case REPORT_LOCATION:
-                return "REPORT_LOCATION";
-            case REPORT_SV_STATUS:
-                return "REPORT_SV_STATUS";
-            default:
-                return "<Unknown>";
+        });
+        if (!success) {
+            mWakeLock.release();
         }
     }
 
@@ -1476,7 +1410,7 @@
 
     @Override
     public void onReportLocation(boolean hasLatLong, Location location) {
-        sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
+        postWithWakeLockHeld(() -> handleReportLocation(hasLatLong, location));
     }
 
     @Override
@@ -1502,7 +1436,7 @@
 
     @Override
     public void onReportSvStatus(GnssStatus gnssStatus) {
-        sendMessage(REPORT_SV_STATUS, 0, gnssStatus);
+        postWithWakeLockHeld(() -> handleReportSvStatus(gnssStatus));
     }
 
     @Override
@@ -1512,7 +1446,7 @@
 
     @Override
     public void onRequestPsdsDownload(int psdsType) {
-        sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+        postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
     }
 
     @Override
@@ -1558,7 +1492,7 @@
                     + ", isUserEmergency: "
                     + isUserEmergency);
         }
-        sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency);
+        postWithWakeLockHeld(() -> handleRequestLocation(independentFromGnss, isUserEmergency));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 21946ca..5de9cf3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -331,7 +331,7 @@
         @Override
         public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
                 GnssCapabilities newCapabilities) {
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
                         .putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
index 9874ecf..7369eac 100644
--- a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
@@ -41,7 +41,7 @@
     public void addListener(DeviceIdleInternal.StationaryListener listener) {
         Preconditions.checkState(mDeviceIdle != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mDeviceIdle.registerStationaryListener(listener);
         } finally {
@@ -53,7 +53,7 @@
     public void removeListener(DeviceIdleInternal.StationaryListener listener) {
         Preconditions.checkState(mDeviceIdle != null);
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             mDeviceIdle.unregisterStationaryListener(listener);
         } finally {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7d5b7e5..fab11a1 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1620,7 +1620,7 @@
                             + PERMISSION);
         }
 
-        long identity = Binder.clearCallingIdentity();
+        final long identity = Binder.clearCallingIdentity();
         try {
             enforceFrpResolved();
             // When changing credential for profiles with unified challenge, some callers
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 2519bbf..8e7c4ff 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.MediaMetrics;
 import android.media.metrics.IMediaMetricsManager;
 import android.media.metrics.NetworkEvent;
 import android.media.metrics.PlaybackErrorEvent;
@@ -65,6 +66,8 @@
     private static final int LOGGING_LEVEL_NO_UID = 1000;
     private static final int LOGGING_LEVEL_BLOCKED = 99999;
 
+    private static final String mMetricsId = MediaMetrics.Name.METRICS_MANAGER;
+
     private static final String FAILED_TO_GET = "failed_to_get";
     private final SecureRandom mSecureRandom;
     @GuardedBy("mLock")
@@ -199,6 +202,12 @@
             mSecureRandom.nextBytes(byteId);
             String id = Base64.encodeToString(
                     byteId, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE);
+
+            // Authorize these session ids in the native mediametrics service.
+            new MediaMetrics.Item(mMetricsId)
+                    .set(MediaMetrics.Property.EVENT, "create")
+                    .set(MediaMetrics.Property.LOG_SESSION_ID, id)
+                    .record();
             return id;
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 42b7c9d3..8a33299 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -354,6 +354,12 @@
         }
     }
 
+    public void unregisterFileCleanupReceiver() {
+        if(mContext != null) {
+            mContext.unregisterReceiver(mFileCleanupReceiver);
+        }
+    }
+
     private static long safeParseLong(String fileName) {
         // AtomicFile will create copies of the numeric files with ".new" and ".bak"
         // over the course of its processing. If these files still exist on boot we need to clean
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6da898a..0aacd13 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -288,6 +288,7 @@
 
     private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
         userHistory.disableHistory();
+        userHistory.unregisterFileCleanupReceiver();
 
         mUserPendingHistoryDisables.put(userId, false);
         mHistoryEnabled.put(userId, false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b54e8f9..74ef4ca 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2320,14 +2320,16 @@
 
         mUserProfiles.updateCache(getContext());
 
-        telephonyManager.listen(new PhoneStateListener() {
-            @Override
-            public void onCallStateChanged(int state, String incomingNumber) {
-                if (mCallState == state) return;
-                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
-                mCallState = state;
-            }
-        }, PhoneStateListener.LISTEN_CALL_STATE);
+        if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            telephonyManager.listen(new PhoneStateListener() {
+                @Override
+                public void onCallStateChanged(int state, String incomingNumber) {
+                    if (mCallState == state) return;
+                    if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+                    mCallState = state;
+                }
+            }, PhoneStateListener.LISTEN_CALL_STATE);
+        }
 
         mSettingsObserver = new SettingsObserver(mHandler);
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f66cfa9..7a00b86 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -585,7 +585,8 @@
                     pw.println("null");
                 } else {
                     pw.print(val.getClass().getSimpleName());
-                    if (redact && (val instanceof CharSequence || val instanceof String)) {
+                    if (redact && (val instanceof CharSequence) && shouldRedactStringExtra(key)) {
+                        pw.print(String.format(" [length=%d]", ((CharSequence) val).length()));
                         // redact contents from bugreports
                     } else if (val instanceof Bitmap) {
                         pw.print(String.format(" (%dx%d)",
@@ -611,6 +612,19 @@
         }
     }
 
+    private boolean shouldRedactStringExtra(String key) {
+        if (key == null) return true;
+        switch (key) {
+            // none of these keys contain user-related information; they do not need to be redacted
+            case Notification.EXTRA_SUBSTITUTE_APP_NAME:
+            case Notification.EXTRA_TEMPLATE:
+            case "android.support.v4.app.extra.COMPAT_TEMPLATE":
+                return false;
+            default:
+                return true;
+        }
+    }
+
     @Override
     public final String toString() {
         return String.format(
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff75..3ca6a0d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -429,4 +429,14 @@
         return NotificationChannelLogger.getLoggingImportance(channel, importance);
     }
 
+    /**
+     * @param r NotificationRecord
+     * @return Whether the notification is a foreground service notification.
+     */
+    static boolean isForegroundService(@NonNull NotificationRecord r) {
+        if (r.getSbn() == null || r.getSbn().getNotification() == null) {
+            return false;
+        }
+        return (r.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e..249910d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -63,7 +63,12 @@
                 /* android.stats.sysui.NotificationImportance importance_asst = 19 */
                 r.getAssistantImportance(),
                 /* int32 assistant_hash = 20 */ p.getAssistantHash(),
-                /* float assistant_ranking_score = 21 */ r.getRankingScore()
+                /* float assistant_ranking_score = 21 */ r.getRankingScore(),
+                /* bool is_ongoing = 22 */ r.getSbn().isOngoing(),
+                /* bool is_foreground_service = 23 */
+                NotificationRecordLogger.isForegroundService(r),
+                /* optional int64 timeout_millis = 24 */
+                r.getSbn().getNotification().getTimeoutAfter()
         );
     }
 
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index f47aa48..0a69aec 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -39,6 +39,9 @@
 
     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
     private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
+    private static final int CHIRP_LEVEL_DURATION_MILLIS = 100;
+    private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100;
+    private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50;
 
     private final Vibrator mVibrator;
     private final long[] mDefaultPattern;
@@ -102,6 +105,9 @@
      * @param insistent {@code true} if the vibration should loop until it is cancelled.
      */
     public VibrationEffect createFallbackVibration(boolean insistent) {
+        if (mVibrator.hasFrequencyControl()) {
+            return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent);
+        }
         return createWaveformVibration(mFallbackPattern, insistent);
     }
 
@@ -111,9 +117,32 @@
      * @param insistent {@code true} if the vibration should loop until it is cancelled.
      */
     public VibrationEffect createDefaultVibration(boolean insistent) {
+        if (mVibrator.hasFrequencyControl()) {
+            return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent);
+        }
         return createWaveformVibration(mDefaultPattern, insistent);
     }
 
+    private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
+        VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
+                .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
+                .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
+                .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
+                .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
+
+        if (insistent) {
+            return waveformBuilder
+                    .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
+                    .build(/* repeat= */ 0);
+        }
+
+        VibrationEffect singleBeat = waveformBuilder.build();
+        return VibrationEffect.startComposition()
+                .addEffect(singleBeat)
+                .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS)
+                .compose();
+    }
+
     private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
         int[] ar = resources.getIntArray(resId);
         if (ar == null) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 53bf7b8..6ae4dd0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -31,10 +31,11 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -583,8 +584,8 @@
                         }
                         factoryPackagesSet.add(packageInfo.packageName);
                     }
-                } else if (throwable instanceof PackageParserException) {
-                    final PackageParserException e = (PackageParserException) throwable;
+                } else if (throwable instanceof PackageManagerException) {
+                    final PackageManagerException e = (PackageManagerException) throwable;
                     // Skip parsing non-coreApp apex file if system is in minimal boot state.
                     if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) {
                         Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath);
@@ -988,15 +989,17 @@
         }
 
         private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException {
-            int minSignatureScheme =
+            final int minSignatureScheme =
                     ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                             pkg.applicationInfo.targetSdkVersion);
-            try {
-                return ApkSignatureVerifier.verify(pkg.applicationInfo.sourceDir,
-                        minSignatureScheme);
-            } catch (PackageParserException e) {
-                throw PackageManagerException.from(e);
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify(
+                    input, pkg.applicationInfo.sourceDir, minSignatureScheme);
+            if (result.isError()) {
+                throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+                        result.getException());
             }
+            return result.getResult();
         }
 
         private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 2851107..a44cad8 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -35,9 +35,11 @@
 import android.content.pm.Checksum;
 import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -461,8 +463,8 @@
                 }
 
                 // Obtaining array of certificates used for signing the installer package.
-                certs = installer.getSigningDetails().signatures;
-                pastCerts = installer.getSigningDetails().pastSigningCertificates;
+                certs = installer.getSigningDetails().getSignatures();
+                pastCerts = installer.getSigningDetails().getPastSigningCertificates();
             }
             if (certs == null || certs.length == 0 || certs[0] == null) {
                 Slog.e(TAG, "Can't obtain certificates.");
@@ -662,14 +664,16 @@
     private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
             String split, String filePath, int types) {
         Map<Integer, byte[]> contentDigests = null;
-        try {
-            contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
-                    PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
-                    false).contentDigests;
-        } catch (PackageParser.PackageParserException e) {
-            if (!(e.getCause() instanceof SignatureNotFoundException)) {
-                Slog.e(TAG, "Signature verification error", e);
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<ApkSignatureVerifier.SigningDetailsWithDigests> result =
+                ApkSignatureVerifier.verifySignaturesInternal(input, filePath,
+                        SignatureSchemeVersion.SIGNING_BLOCK_V2, false /*verifyFull*/);
+        if (result.isError()) {
+            if (!(result.getException() instanceof SignatureNotFoundException)) {
+                Slog.e(TAG, "Signature verification error", result.getException());
             }
+        } else {
+            contentDigests = result.getResult().contentDigests;
         }
 
         if (contentDigests == null) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff691..56b77b5 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -28,7 +28,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -147,7 +147,7 @@
     private final OverlayReferenceMapper mOverlayReferenceMapper;
     private final StateProvider mStateProvider;
 
-    private PackageParser.SigningDetails mSystemSigningDetails;
+    private SigningDetails mSystemSigningDetails;
     private Set<String> mProtectedBroadcasts = new ArraySet<>();
 
     private final Object mCacheLock = new Object();
@@ -951,7 +951,7 @@
         }
     }
 
-    private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
+    private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
             PackageSetting pkgSetting) {
         return pkgSetting.isSystem()
                 && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
@@ -1287,20 +1287,6 @@
                 }
             }
 
-            // This package isn't technically installed and won't be written to settings, so we can
-            // treat it as filtered until it's available again.
-            final AndroidPackage targetPkg = targetPkgSetting.pkg;
-            if (targetPkg == null) {
-                if (DEBUG_LOGGING) {
-                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
-                }
-                return true;
-            }
-            if (targetPkg.isStaticSharedLibrary()) {
-                // not an app, this filtering takes place at a higher level
-                return false;
-            }
-            final String targetName = targetPkg.getPackageName();
             if (DEBUG_TRACING) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
             }
@@ -1343,6 +1329,21 @@
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             }
+
+            // This package isn't technically installed and won't be written to settings, so we can
+            // treat it as filtered until it's available again.
+            final AndroidPackage targetPkg = targetPkgSetting.pkg;
+            if (targetPkg == null) {
+                if (DEBUG_LOGGING) {
+                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
+                }
+                return true;
+            }
+            if (targetPkg.isStaticSharedLibrary()) {
+                // not an app, this filtering takes place at a higher level
+                return false;
+            }
+
             try {
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
@@ -1415,6 +1416,7 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
                 }
+                final String targetName = targetPkg.getPackageName();
                 if (callingSharedPkgSettings != null) {
                     int size = callingSharedPkgSettings.size();
                     for (int index = 0; index < size; index++) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3019439..fb68b23 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -178,6 +178,16 @@
                     .addDataType("*/*")
                     .build();
 
+    /** Pick images can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */ 0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_PICK_IMAGES)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
     /** Open document intent can be forwarded to parent user. */
     private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
             new DefaultCrossProfileIntentFilter.Builder(
@@ -289,6 +299,7 @@
                 RECOGNIZE_SPEECH,
                 ACTION_PICK_RAW,
                 ACTION_PICK_DATA,
+                ACTION_PICK_IMAGES,
                 OPEN_DOCUMENT,
                 GET_CONTENT,
                 USB_DEVICE_ATTACHED,
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index bf323e7..affacbb 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -23,8 +23,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -412,14 +412,14 @@
             // into account but also allow the value from the old computation to avoid
             // data loss.
             if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
-                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+                    SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                 return;
             }
 
             // For backwards compatibility we accept match based on any signature, since we may have
             // recorded only the first for multiply-signed packages
-            final String[] signaturesSha256Digests =
-                    PackageUtils.computeSignaturesSha256Digests(pkg.getSigningDetails().signatures);
+            final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
+                    pkg.getSigningDetails().getSignatures());
             for (String s : signaturesSha256Digests) {
                 if (s.equals(currentCookieSha256)) {
                     return;
@@ -1305,8 +1305,8 @@
             // We prefer the modern computation procedure where all certs are taken
             // into account and delete the file derived via the legacy hash computation.
             File newCookieFile = computeInstantCookieFile(pkg.getPackageName(),
-                    PackageUtils.computeSignaturesSha256Digest(pkg.getSigningDetails().signatures),
-                    userId);
+                    PackageUtils.computeSignaturesSha256Digest(
+                            pkg.getSigningDetails().getSignatures()), userId);
             if (!pkg.getSigningDetails().hasSignatures()) {
                 Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
             }
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 34caaf5..fe74889 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -193,7 +193,7 @@
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Passed invalid package to keyset validation.");
         }
-        ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().publicKeys;
+        ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().getPublicKeys();
         if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Package has invalid signing-key-set.");
@@ -226,7 +226,7 @@
         PackageSetting ps = mPackages.get(pkg.getPackageName());
         Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
                     + "does not have a corresponding entry in mPackages.");
-        addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
+        addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().getPublicKeys());
         if (pkg.getKeySetMapping() != null) {
             addDefinedKeySetsToPackageLPw(ps, pkg.getKeySetMapping());
             if (pkg.getUpgradeKeySets() != null) {
@@ -371,7 +371,7 @@
         for (int i = 0; i < upgradeKeySets.length; i++) {
             Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
             if (upgradeSet != null
-                    && pkg.getSigningDetails().publicKeys.containsAll(upgradeSet)) {
+                    && pkg.getSigningDetails().getPublicKeys().containsAll(upgradeSet)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 419b726..6f02138 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -407,11 +407,16 @@
 
         @Override
         public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
-            if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+            final int userId = user.getIdentifier();
+            if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
                 return false;
             }
-            final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(packageName,
-                    user.getIdentifier());
+            if (mPackageManagerInternal.filterAppAccess(
+                    packageName, Binder.getCallingUid(), userId)) {
+                return false;
+            }
+            final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(
+                    packageName, userId);
             return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
         }
 
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 43c5d5e..3233819 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -42,7 +42,7 @@
 per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
 per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
 per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
-per-file SELinuxMMAC.java = cbrubaker@google.com, jeffv@google.com, jgalenson@google.com, nnk@google.com
+per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com, jgalenson@google.com
 
 # shortcuts
 per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d7b2449..fe419a6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -254,7 +254,8 @@
         mSessionsDir.mkdirs();
 
         mApexManager = ApexManager.getInstance();
-        mStagingManager = new StagingManager(context, apexParserSupplier);
+        mStagingManager = new StagingManager(context, apexParserSupplier,
+                mInstallThread.getLooper());
 
         LocalServices.getService(SystemServiceManager.class).startService(
                 new Lifecycle(context, this));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5429484..a848536 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -46,9 +46,11 @@
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 
 import android.Manifest;
+import android.annotation.AnyThread;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -82,8 +84,7 @@
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.SigningDetails;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
@@ -263,6 +264,15 @@
     private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
     private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
 
+    /**
+     * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
+     * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#Q} before getting the
+     * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
+     * result will not trigger any user action in
+     * {@link #checkUserActionRequirement(PackageInstallerSession)}.
+     */
+    private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
+
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
 
@@ -366,7 +376,7 @@
     @GuardedBy("mLock")
     private long mVersionCode;
     @GuardedBy("mLock")
-    private PackageParser.SigningDetails mSigningDetails;
+    private SigningDetails mSigningDetails;
     @GuardedBy("mLock")
     private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
     @GuardedBy("mLock")
@@ -705,28 +715,22 @@
         }
 
         /**
-         * Notified by the staging manager that pre-reboot verification is about to start. The
-         * return value should be checked to decide whether it is OK to start pre-reboot
-         * verification. In the case of a destroyed session, {@code false} is returned and there is
-         * no need to start pre-reboot verification.
+         * Called when pre-reboot verification is about to start. This shouldn't be called
+         * on a destroyed session.
          */
-        @Override
-        public boolean notifyStartPreRebootVerification() {
+        private void notifyStartPreRebootVerification() {
             synchronized (mLock) {
+                Preconditions.checkState(!mDestroyed);
                 if (mInPreRebootVerification) {
                     throw new IllegalStateException("Pre-reboot verification has started");
                 }
-                if (mDestroyed) {
-                    return false;
-                }
                 mInPreRebootVerification = true;
-                return true;
             }
         }
 
         /**
-         * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
-         * clean up the session if {@link #abandon()} has been called previously.
+         * Notified by the staging manager or PIS that pre-reboot verification has ended.
+         * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
          */
         @Override
         public void notifyEndPreRebootVerification() {
@@ -750,6 +754,7 @@
             assertCallerIsOwnerOrRootOrSystem();
             Preconditions.checkArgument(isCommitted());
             Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+            notifyStartPreRebootVerification();
             verify();
         }
 
@@ -803,6 +808,12 @@
     @GuardedBy("mLock")
     private PackageLite mPackageLite;
 
+    /**
+     * Keep the target sdk of a validated apk.
+     */
+    @GuardedBy("mLock")
+    private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
+
     private static final FileFilter sAddedApkFilter = new FileFilter() {
         @Override
         public boolean accept(File file) {
@@ -1736,6 +1747,7 @@
         mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
     }
 
+    @WorkerThread
     private void handleStreamValidateAndCommit() {
         PackageManagerException unrecoverableFailure = null;
         // This will track whether the session and any children were validated and are ready to
@@ -1985,6 +1997,7 @@
      * exception is thrown.
      * @throws PackageManagerException on an unrecoverable error.
      */
+    @WorkerThread
     private boolean streamValidateAndCommit() throws PackageManagerException {
         // TODO(patb): since the work done here for a parent session in a multi-package install is
         //             mostly superficial, consider splitting this method for the parent and
@@ -2102,9 +2115,7 @@
         if (isStaged()) {
             mStagedSession.setSessionFailed(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
-            // TODO(b/136257624): Remove this once all verification logic has been transferred out
-            //  of StagingManager.
-            mStagingManager.notifyVerificationComplete(mStagedSession);
+            mStagedSession.notifyEndPreRebootVerification();
         } else {
             // Dispatch message to remove session from PackageInstallerService.
             dispatchSessionFinished(error, msg, null);
@@ -2148,6 +2159,7 @@
      *                    immutable by the caller during the method call. Used to resolve child
      *                    sessions Ids to actual object reference.
      */
+    @AnyThread
     void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
         synchronized (mLock) {
             // Resolve null values to actual object references
@@ -2233,6 +2245,63 @@
         }
     }
 
+    @WorkerThread
+    private static boolean checkUserActionRequirement(PackageInstallerSession session) {
+        if (session.isMultiPackage()) {
+            return false;
+        }
+
+        @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
+        // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
+        userActionRequirement = session.computeUserActionRequirement();
+        if (userActionRequirement == USER_ACTION_REQUIRED) {
+            session.sendPendingUserActionIntent();
+            return true;
+        }
+
+        if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
+            final int validatedTargetSdk;
+            synchronized (session.mLock) {
+                validatedTargetSdk = session.mValidatedTargetSdk;
+            }
+
+            if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION
+                    && validatedTargetSdk < Build.VERSION_CODES.Q) {
+                session.sendPendingUserActionIntent();
+                return true;
+            }
+
+            if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
+                if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
+                        session.getInstallerPackageName(), session.getPackageName())) {
+                    // Fall back to the non-silent update if a repeated installation is invoked
+                    // within the throttle time.
+                    session.sendPendingUserActionIntent();
+                    return true;
+                }
+                session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
+                        session.getPackageName());
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Find out any session needs user action.
+     *
+     * @return true if the session set requires user action for the installation, otherwise false.
+     */
+    @WorkerThread
+    private boolean sendPendingUserActionIntentIfNeeded() {
+        synchronized (mLock) {
+            assertNotChildLocked("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+        }
+
+        return sessionContains(PackageInstallerSession::checkUserActionRequirement);
+    }
+
+    @WorkerThread
     private void handleInstall() {
         if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
             DevicePolicyEventLogger
@@ -2241,6 +2310,17 @@
                     .write();
         }
 
+        /**
+         * Stops the installation of the whole session set if one session needs user action
+         * in its belong session set. When the user answers the yes,
+         * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
+         * handled to come back here to check again.
+         */
+        if (sendPendingUserActionIntentIfNeeded()) {
+            return;
+        }
+
+
         // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
         // the places that:
         //   * Shared between staged and non-staged APEX update flows.
@@ -2278,14 +2358,14 @@
         }
 
         if (params.isStaged) {
-            mStagingManager.commitSession(mStagedSession);
             // 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(INSTALL_SUCCEEDED, "Session staged", null);
-            return;
-        }
 
-        verify();
+            mStagedSession.verifySession();
+        } else {
+            verify();
+        }
     }
 
     private void verify() {
@@ -2297,18 +2377,18 @@
         }
     }
 
+    private IntentSender getRemoteStatusReceiver() {
+        synchronized (mLock) {
+            return mRemoteStatusReceiver;
+        }
+    }
+
     private void verifyNonStaged()
             throws PackageManagerException {
         final PackageManagerService.VerificationParams verifyingSession =
                 prepareForVerification();
-        if (verifyingSession == null) {
-            return;
-        }
         if (isMultiPackage()) {
-            final List<PackageInstallerSession> childSessions;
-            synchronized (mLock) {
-                childSessions = getChildSessionsLocked();
-            }
+            final List<PackageInstallerSession> childSessions = getChildSessions();
             // Spot check to reject a non-staged multi package install of APEXes and APKs.
             if (!params.isStaged && containsApkSession()
                     && sessionContains(s -> s.isApexSession())) {
@@ -2325,20 +2405,14 @@
                 try {
                     final PackageManagerService.VerificationParams verifyingChildSession =
                             session.prepareForVerification();
-                    if (verifyingChildSession != null) {
-                        verifyingChildSessions.add(verifyingChildSession);
-                    }
+                    verifyingChildSessions.add(verifyingChildSession);
                 } catch (PackageManagerException e) {
                     failure = e;
                     success = false;
                 }
             }
             if (!success) {
-                final IntentSender statusReceiver;
-                synchronized (mLock) {
-                    statusReceiver = mRemoteStatusReceiver;
-                }
-                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+                sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
                         isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                         failure.error, failure.getLocalizedMessage(), null);
                 return;
@@ -2366,10 +2440,7 @@
                     "Session should contain at least one apk session for installation");
         }
         if (isMultiPackage()) {
-            final List<PackageInstallerSession> childSessions;
-            synchronized (mLock) {
-                childSessions = getChildSessionsLocked();
-            }
+            final List<PackageInstallerSession> childSessions = getChildSessions();
             List<PackageManagerService.InstallParams> installingChildSessions =
                     new ArrayList<>(childSessions.size());
             boolean success = true;
@@ -2388,11 +2459,7 @@
                 }
             }
             if (!success) {
-                final IntentSender statusReceiver;
-                synchronized (mLock) {
-                    statusReceiver = mRemoteStatusReceiver;
-                }
-                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+                sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
                         isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                         failure.error, failure.getLocalizedMessage(), null);
                 return;
@@ -2408,23 +2475,11 @@
      * {@link PackageManagerService.VerificationParams} representing this new staged state or null
      * in case permissions need to be requested before verification can proceed.
      */
-    @Nullable
+    @NonNull
     private PackageManagerService.VerificationParams prepareForVerification()
             throws PackageManagerException {
         assertNotLocked("makeSessionActive");
 
-        @UserActionRequirement
-        int userActionRequirement = USER_ACTION_NOT_NEEDED;
-        // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
-        if (!params.isMultiPackage) {
-            userActionRequirement = computeUserActionRequirement();
-            if (userActionRequirement == USER_ACTION_REQUIRED) {
-                sendPendingUserActionIntent();
-                return null;
-            } // else, we'll wait until we parse to determine if we need to
-        }
-
-        boolean silentUpdatePolicyEnforceable = false;
         synchronized (mLock) {
             if (mRelinquished) {
                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2441,36 +2496,16 @@
             PackageLite result = parseApkLite();
             if (result != null) {
                 mPackageLite = result;
-                synchronized (mProgressLock) {
-                    mInternalProgress = 0.5f;
-                    computeProgressLocked(true);
-                }
-
-                extractNativeLibraries(
-                        mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
-
-                if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
-                    if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
-                        sendPendingUserActionIntent();
-                        return null;
+                if (!isApexSession()) {
+                    synchronized (mProgressLock) {
+                        mInternalProgress = 0.5f;
+                        computeProgressLocked(true);
                     }
-                    if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
-                        silentUpdatePolicyEnforceable = true;
-                    }
+
+                    extractNativeLibraries(
+                            mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
                 }
             }
-        }
-        if (silentUpdatePolicyEnforceable) {
-            if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
-                    getInstallerPackageName(), getPackageName())) {
-                // Fall back to the non-silent update if a repeated installation is invoked within
-                // the throttle time.
-                sendPendingUserActionIntent();
-                return null;
-            }
-            mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
-        }
-        synchronized (mLock) {
             return makeVerificationParamsLocked();
         }
     }
@@ -2483,11 +2518,7 @@
         intent.setPackage(mPm.getPackageInstallerPackageName());
         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
 
-        final IntentSender statusReceiver;
-        synchronized (mLock) {
-            statusReceiver = mRemoteStatusReceiver;
-        }
-        sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+        sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent);
 
         // Commit was keeping session marked as active until now; release
         // that extra refcount so session appears idle.
@@ -2497,73 +2528,77 @@
     /**
      * Prepares staged directory with any inherited APKs and returns the parsed package.
      */
+    @GuardedBy("mLock")
     @Nullable
     private PackageLite parseApkLite() throws PackageManagerException {
 
 
-        // TODO(b/136257624): Some logic in this if block probably belongs in
-        //  makeInstallParams().
-        if (!isMultiPackage() && !isApexSession()) {
-            Objects.requireNonNull(mPackageName);
-            Objects.requireNonNull(mSigningDetails);
-            Objects.requireNonNull(mResolvedBaseFile);
+        if (isMultiPackage()) {
+            return null;
+        }
+        Objects.requireNonNull(mPackageName);
+        Objects.requireNonNull(mSigningDetails);
+        Objects.requireNonNull(mResolvedBaseFile);
 
-            // If we haven't already parsed, inherit any packages and native libraries from existing
-            // install that haven't been overridden.
-            if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-                try {
-                    final List<File> fromFiles = mResolvedInheritedFiles;
-                    final File toDir = stageDir;
+        // Inherit any packages and native libraries from existing install that
+        // haven't been overridden.
+        if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+            try {
+                final List<File> fromFiles = mResolvedInheritedFiles;
+                final File toDir = stageDir;
 
-                    if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
-                    if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
-                        throw new IllegalStateException("mInheritedFilesBase == null");
-                    }
-
-                    if (isLinkPossible(fromFiles, toDir)) {
-                        if (!mResolvedInstructionSets.isEmpty()) {
-                            final File oatDir = new File(toDir, "oat");
-                            createOatDirs(mResolvedInstructionSets, oatDir);
-                        }
-                        // pre-create lib dirs for linking if necessary
-                        if (!mResolvedNativeLibPaths.isEmpty()) {
-                            for (String libPath : mResolvedNativeLibPaths) {
-                                // "/lib/arm64" -> ["lib", "arm64"]
-                                final int splitIndex = libPath.lastIndexOf('/');
-                                if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
-                                    Slog.e(TAG,
-                                            "Skipping native library creation for linking due"
-                                                    + " to invalid path: " + libPath);
-                                    continue;
-                                }
-                                final String libDirPath = libPath.substring(1, splitIndex);
-                                final File libDir = new File(toDir, libDirPath);
-                                if (!libDir.exists()) {
-                                    NativeLibraryHelper.createNativeLibrarySubdir(libDir);
-                                }
-                                final String archDirPath = libPath.substring(splitIndex + 1);
-                                NativeLibraryHelper.createNativeLibrarySubdir(
-                                        new File(libDir, archDirPath));
-                            }
-                        }
-                        linkFiles(fromFiles, toDir, mInheritedFilesBase);
-                    } else {
-                        // TODO: this should delegate to DCS so the system process
-                        // avoids holding open FDs into containers.
-                        copyFiles(fromFiles, toDir);
-                    }
-                } catch (IOException e) {
-                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                            "Failed to inherit existing install", e);
+                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+                    throw new IllegalStateException("mInheritedFilesBase == null");
                 }
+
+                if (isLinkPossible(fromFiles, toDir)) {
+                    if (!mResolvedInstructionSets.isEmpty()) {
+                        final File oatDir = new File(toDir, "oat");
+                        createOatDirs(mResolvedInstructionSets, oatDir);
+                    }
+                    // pre-create lib dirs for linking if necessary
+                    if (!mResolvedNativeLibPaths.isEmpty()) {
+                        for (String libPath : mResolvedNativeLibPaths) {
+                            // "/lib/arm64" -> ["lib", "arm64"]
+                            final int splitIndex = libPath.lastIndexOf('/');
+                            if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+                                Slog.e(TAG,
+                                        "Skipping native library creation for linking due"
+                                                + " to invalid path: " + libPath);
+                                continue;
+                            }
+                            final String libDirPath = libPath.substring(1, splitIndex);
+                            final File libDir = new File(toDir, libDirPath);
+                            if (!libDir.exists()) {
+                                NativeLibraryHelper.createNativeLibrarySubdir(libDir);
+                            }
+                            final String archDirPath = libPath.substring(splitIndex + 1);
+                            NativeLibraryHelper.createNativeLibrarySubdir(
+                                    new File(libDir, archDirPath));
+                        }
+                    }
+                    linkFiles(fromFiles, toDir, mInheritedFilesBase);
+                } else {
+                    // TODO: this should delegate to DCS so the system process
+                    // avoids holding open FDs into containers.
+                    copyFiles(fromFiles, toDir);
+                }
+            } catch (IOException e) {
+                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                        "Failed to inherit existing install", e);
             }
+        }
+
+        if (!isApexSession()) {
             // For mode inherit existing, it would link/copy existing files to stage dir in the
             // above block. Therefore, we need to parse the complete package in stage dir here.
-            // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
-            // verification.
+            // Besides, PackageLite may be null for staged sessions that don't complete
+            // pre-reboot verification.
             return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+        } else {
+            return getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
         }
-        return null;
     }
 
     @GuardedBy("mLock")
@@ -2604,25 +2639,16 @@
 
         mRelinquished = true;
 
-        // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
-        PackageInstaller.SessionParams copiedParams = params.copy();
-        if (params.isStaged) {
-            // This is called by the pre-reboot verification. Don't enable rollback here since
-            // it has been enabled when pre-reboot verification starts.
-            copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
-        }
-        return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
+        return mPm.new VerificationParams(user, stageDir, localObserver, params,
                 mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
     }
 
     private void onVerificationComplete() {
-        // Staged sessions will be installed later during boot
+        // APK verification is done. Continue the installation depending on whether it is a
+        // staged session or not. For a staged session, we will hand it over to the staging
+        // manager to complete the installation.
         if (isStaged()) {
-            // TODO(b/136257624): Remove this once all verification logic has been transferred out
-            //  of StagingManager.
-            mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
-            // TODO(b/136257624): We also need to destroy internals for verified staged session,
-            //  otherwise file descriptors are never closed for verified staged session until reboot
+            mStagingManager.commitSession(mStagedSession);
             return;
         }
 
@@ -2886,6 +2912,8 @@
             mPackageName = apk.getPackageName();
             mVersionCode = apk.getLongVersionCode();
         }
+
+        mSigningDetails = apk.getSigningDetails();
     }
 
     /**
@@ -2904,11 +2932,11 @@
     @GuardedBy("mLock")
     private PackageLite validateApkInstallLocked() throws PackageManagerException {
         ApkLite baseApk = null;
-        PackageLite packageLite = null;
+        final PackageLite packageLite;
         mPackageLite = null;
         mPackageName = null;
         mVersionCode = -1;
-        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+        mSigningDetails = SigningDetails.UNKNOWN;
 
         mResolvedBaseFile = null;
         mResolvedStagedFiles.clear();
@@ -2971,7 +2999,7 @@
                 mPackageName = apk.getPackageName();
                 mVersionCode = apk.getLongVersionCode();
             }
-            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+            if (mSigningDetails == SigningDetails.UNKNOWN) {
                 mSigningDetails = apk.getSigningDetails();
             }
 
@@ -3026,7 +3054,7 @@
                 mPackageName = pkgInfo.packageName;
                 mVersionCode = pkgInfo.getLongVersionCode();
             }
-            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+            if (mSigningDetails == SigningDetails.UNKNOWN) {
                 mSigningDetails = unsafeGetCertsWithoutVerification(
                         pkgInfo.applicationInfo.sourceDir);
             }
@@ -3086,7 +3114,7 @@
             packageLite = existing;
             assertPackageConsistentLocked("Existing", existing.getPackageName(),
                     existing.getLongVersionCode());
-            final PackageParser.SigningDetails signingDetails =
+            final SigningDetails signingDetails =
                     unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
             if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3211,6 +3239,11 @@
                 mIncrementalFileStorages.disallowReadLogs();
             }
         }
+
+        // {@link #sendPendingUserActionIntentIfNeeded} needs to use
+        // {@link PackageLite#getTargetSdk()}
+        mValidatedTargetSdk = packageLite.getTargetSdk();
+
         return packageLite;
     }
 
@@ -3426,15 +3459,17 @@
         }
     }
 
-    private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path)
+    private SigningDetails unsafeGetCertsWithoutVerification(String path)
             throws PackageManagerException {
-        try {
-            return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path,
-                    PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
-        } catch (PackageParserException e) {
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<SigningDetails> result =
+                ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+                        input, path, SigningDetails.SignatureSchemeVersion.JAR);
+        if (result.isError()) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Couldn't obtain signatures from APK : " + path);
         }
+        return result.getResult();
     }
 
     /**
@@ -3622,8 +3657,13 @@
             // Mark and kick off another install pass
             synchronized (mLock) {
                 mPermissionsManuallyAccepted = true;
-                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
             }
+
+            PackageInstallerSession root =
+                    (hasParentSessionId())
+                            ? mSessionProvider.getSession(getParentSessionId())
+                            : this;
+            root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
         } else {
             destroyInternal();
             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -3909,11 +3949,7 @@
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
                             // Don't fail or commit the session. Allow caller to commit again.
-                            final IntentSender statusReceiver;
-                            synchronized (mLock) {
-                                statusReceiver = mRemoteStatusReceiver;
-                            }
-                            sendPendingStreaming(mContext, statusReceiver, sessionId,
+                            sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
                                     "DataLoader unavailable");
                             break;
                         }
@@ -3927,11 +3963,8 @@
                 } catch (RemoteException e) {
                     // In case of streaming failure we don't want to fail or commit the session.
                     // Just return from this method and allow caller to commit again.
-                    final IntentSender statusReceiver;
-                    synchronized (mLock) {
-                        statusReceiver = mRemoteStatusReceiver;
-                    }
-                    sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
+                    sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
+                            e.getMessage());
                 }
             }
         };
@@ -4200,18 +4233,13 @@
     }
 
     private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
-        final IntentSender statusReceiver;
-        final String packageName;
-        synchronized (mLock) {
-            statusReceiver = mRemoteStatusReceiver;
-            packageName = mPackageName;
-        }
+        final IntentSender statusReceiver = getRemoteStatusReceiver();
         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.arg1 = getPackageName();
             args.arg2 = msg;
             args.arg3 = extras;
             args.arg4 = statusReceiver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index 0793b09..4662389 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -17,7 +17,6 @@
 package com.android.server.pm;
 
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
 
 import com.android.server.pm.Installer.InstallerException;
 
@@ -45,11 +44,6 @@
         this.error = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
     }
 
-    public static PackageManagerException from(PackageParserException e)
-            throws PackageManagerException {
-        throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
-    }
-
     public static PackageManagerException from(InstallerException e)
             throws PackageManagerException {
         throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee44c10..64a0710 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -95,7 +95,7 @@
 import static android.content.pm.PackageManager.TYPE_UNKNOWN;
 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.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
 import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
@@ -132,6 +132,7 @@
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
 import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
 
 import android.Manifest;
 import android.annotation.AppIdInt;
@@ -147,6 +148,7 @@
 import android.app.ApplicationPackageManager;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
+import android.app.PendingIntent;
 import android.app.ResourcesManager;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
@@ -159,6 +161,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -209,9 +212,6 @@
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.PackagePartitions;
 import android.content.pm.PackagePartitions.SystemPartition;
 import android.content.pm.PackageStats;
@@ -226,6 +226,8 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.SigningInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.TestUtilityService;
@@ -238,6 +240,7 @@
 import android.content.pm.dex.IArtManager;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
@@ -1354,7 +1357,6 @@
         public DefaultAppProvider defaultAppProvider;
         public DexManager dexManager;
         public List<ScanPartition> dirsToScanAsSystem;
-        public @Nullable String documenterPackage;
         public boolean factoryTest;
         public ArrayMap<String, FeatureInfo> availableFeatures;
         public Handler handler;
@@ -1703,7 +1705,6 @@
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mDefaultTextClassifierPackage;
     final @Nullable String mSystemTextClassifierPackageName;
-    final @Nullable String mDocumenterPackage;
     final @Nullable String mConfiguratorPackage;
     final @Nullable String mAppPredictionServicePackage;
     final @Nullable String mIncidentReportApproverPackage;
@@ -2131,6 +2132,8 @@
         @LiveImplementation(override = LiveImplementation.MANDATORY)
         boolean filterAppAccess(String packageName, int callingUid, int userId);
         @LiveImplementation(override = LiveImplementation.MANDATORY)
+        boolean filterAppAccess(int uid, int callingUid);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
     }
 
@@ -2623,8 +2626,8 @@
                         a, flags, ps.readUserState(userId), userId, ps);
             }
             if (resolveComponentName().equals(component)) {
-                return PackageParser.generateActivityInfo(
-                        mResolveActivity, flags, new PackageUserState(), userId);
+                return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
+                        flags, new PackageUserState(), userId);
             }
             return null;
         }
@@ -3274,8 +3277,10 @@
                 return result;
             }
             final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
-            ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
-                    instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
+            ephemeralInstaller.activityInfo =
+                    PackageInfoWithoutStateUtils.generateDelegateActivityInfo(
+                            instantAppInstallerActivity(), 0 /*flags*/, ps.readUserState(userId),
+                            userId);
             ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
                     | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
             // add a non-generic filter
@@ -3358,8 +3363,8 @@
                 ai.setVersionCode(ps.versionCode);
                 ai.flags = ps.pkgFlags;
                 ai.privateFlags = ps.pkgPrivateFlags;
-                pi.applicationInfo =
-                        PackageParser.generateApplicationInfo(ai, flags, state, userId);
+                pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
+                        ai, flags, state, userId);
 
                 if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
                         + ps.name + "]. Provides a minimum info.");
@@ -4402,11 +4407,11 @@
             // reader
             final AndroidPackage p = mPackages.get(packageName);
             if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
-                PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
-                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
-                    return -1;
+                final PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
+                if (ps != null && ps.getInstalled(userId)
+                        && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                    return UserHandle.getUid(userId, p.getUid());
                 }
-                return UserHandle.getUid(userId, p.getUid());
             }
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -4654,6 +4659,22 @@
                     userId);
         }
 
+        public boolean filterAppAccess(int uid, int callingUid) {
+            final int userId = UserHandle.getUserId(uid);
+            final int appId = UserHandle.getAppId(uid);
+            final Object setting = mSettings.getSettingLPr(appId);
+
+            if (setting instanceof SharedUserSetting) {
+                return shouldFilterApplicationLocked(
+                        (SharedUserSetting) setting, callingUid, userId);
+            } else if (setting == null
+                    || setting instanceof PackageSetting) {
+                return shouldFilterApplicationLocked(
+                        (PackageSetting) setting, callingUid, userId);
+            }
+            return false;
+        }
+
         public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
             final String packageName = dumpState.getTargetPackageName();
             final boolean checkin = dumpState.isCheckIn();
@@ -5000,6 +5021,11 @@
                 return super.filterAppAccess(packageName, callingUid, userId);
             }
         }
+        public final boolean filterAppAccess(int uid, int callingUid) {
+            synchronized (mLock) {
+                return super.filterAppAccess(uid, callingUid);
+            }
+        }
         public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
             synchronized (mLock) {
                 super.dump(type, fd, pw, dumpState);
@@ -5370,6 +5396,14 @@
                 current.release();
             }
         }
+        public final boolean filterAppAccess(int uid, int callingUid) {
+            ThreadComputer current = snapshot();
+            try {
+                return current.mComputer.filterAppAccess(uid, callingUid);
+            } finally {
+                current.release();
+            }
+        }
         public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
                 int userId, int flags) {
             ThreadComputer current = live();
@@ -5932,7 +5966,8 @@
                         final VerificationParams params = state.getVerificationParams();
                         final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
 
-                        Slog.i(TAG, "Verification timed out for " + originUri);
+                        String errorMsg = "Verification timed out for " + originUri;
+                        Slog.i(TAG, errorMsg);
 
                         final UserHandle user = params.getUser();
                         if (getDefaultVerificationResponse(user)
@@ -5948,7 +5983,7 @@
                                     PackageManager.VERIFICATION_REJECT, null,
                                     params.mDataLoaderType, user);
                             params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
                             state.setVerifierResponse(Binder.getCallingUid(),
                                     PackageManager.VERIFICATION_REJECT);
                         }
@@ -5973,7 +6008,8 @@
                         final VerificationParams params = state.getVerificationParams();
                         final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
 
-                        Slog.i(TAG, "Integrity verification timed out for " + originUri);
+                        String errorMsg = "Integrity verification timed out for " + originUri;
+                        Slog.i(TAG, errorMsg);
 
                         state.setIntegrityVerificationResult(
                                 getDefaultIntegrityVerificationResponse());
@@ -5983,7 +6019,8 @@
                             Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
                         } else {
                             params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                                    errorMsg);
                         }
 
                         if (state.areAllVerificationsComplete()) {
@@ -6023,7 +6060,8 @@
                                     response.code, null, params.mDataLoaderType, params.getUser());
                         } else {
                             params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                                    "Install not allowed");
                         }
 
                         if (state.areAllVerificationsComplete()) {
@@ -6058,7 +6096,8 @@
                         Slog.i(TAG, "Integrity check passed for " + originUri);
                     } else {
                         params.setReturnCode(
-                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "Integrity check failed for " + originUri);
                     }
 
                     if (state.areAllVerificationsComplete()) {
@@ -6989,7 +7028,6 @@
         mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
         mRetailDemoPackage = testParams.retailDemoPackage;
         mRecentsPackage = testParams.recentsPackage;
-        mDocumenterPackage = testParams.documenterPackage;
         mConfiguratorPackage = testParams.configuratorPackage;
         mAppPredictionServicePackage = testParams.appPredictionServicePackage;
         mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -7578,6 +7616,8 @@
             }
             mExpectingBetter.clear();
 
+            mSettings.pruneRenamedPackagesLPw();
+
             // Resolve the storage manager.
             mStorageManagerPackage = getStorageManagerPackageName();
 
@@ -7588,7 +7628,6 @@
 
             mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
             mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
-            mDocumenterPackage = getDocumenterPackageName();
             mConfiguratorPackage = getDeviceConfiguratorPackageName();
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
@@ -7941,7 +7980,7 @@
     private boolean enableCompressedPackage(AndroidPackage stubPkg,
             @NonNull PackageSetting stubPkgSetting) {
         final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
-                | PackageParser.PARSE_ENFORCE_CODE;
+                | ParsingPackageUtils.PARSE_ENFORCE_CODE;
         synchronized (mInstallLock) {
             final AndroidPackage pkg;
             try (PackageFreezer freezer =
@@ -8600,7 +8639,7 @@
                 if (ps != null) {
                     final PackageUserState state = ps.readUserState(userId);
                     if (state != null) {
-                        return PackageParser.isAvailable(state);
+                        return checkUseInstalledOrHidden(p, ps, state, 0 /*flags*/);
                     }
                 }
             }
@@ -8765,13 +8804,11 @@
         synchronized (mLock) {
             final AndroidPackage p = mPackages.get(packageName);
             if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
-                PackageSetting ps = getPackageSetting(p.getPackageName());
-                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
-                    return null;
+                final PackageSetting ps = getPackageSetting(p.getPackageName());
+                if (ps != null && ps.getInstalled(userId)
+                        && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                    return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
                 }
-                // TODO: Shouldn't this be checking for package installed state for userId and
-                // return null?
-                return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
             }
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -9781,7 +9818,8 @@
         if (p2SigningDetails == null) {
             return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
         }
-        int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
+        int result = compareSignatures(p1SigningDetails.getSignatures(),
+                p2SigningDetails.getSignatures());
         if (result == PackageManager.SIGNATURE_MATCH) {
             return result;
         }
@@ -9791,11 +9829,11 @@
         if (p1SigningDetails.hasPastSigningCertificates()
                 || p2SigningDetails.hasPastSigningCertificates()) {
             Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
-                    ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
-                    : p1SigningDetails.signatures;
+                    ? new Signature[]{p1SigningDetails.getPastSigningCertificates()[0]}
+                    : p1SigningDetails.getSignatures();
             Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
-                    ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
-                    : p2SigningDetails.signatures;
+                    ? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
+                    : p2SigningDetails.getSignatures();
             result = compareSignatures(p1Signatures, p2Signatures);
         }
         return result;
@@ -9839,7 +9877,7 @@
         final int appId = UserHandle.getAppId(uid);
         // reader
         synchronized (mLock) {
-            final PackageParser.SigningDetails signingDetails;
+            final SigningDetails signingDetails;
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
@@ -10133,7 +10171,22 @@
         if (getInstantAppPackageName(getCallingUid()) != null) {
             return EmptyArray.STRING;
         }
-        return mPermissionManager.getAppOpPermissionPackages(permissionName);
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+
+        final ArraySet<String> packageNames = new ArraySet(
+                mPermissionManager.getAppOpPermissionPackages(permissionName));
+        synchronized (mLock) {
+            for (int i = packageNames.size() - 1; i >= 0; i--) {
+                final String packageName = packageNames.valueAt(i);
+                if (!shouldFilterApplicationLocked(mSettings.getPackageLPr(packageName),
+                        callingUid, callingUserId)) {
+                    continue;
+                }
+                packageNames.removeAt(i);
+            }
+        }
+        return packageNames.toArray(new String[packageNames.size()]);
     }
 
     @Override
@@ -11846,9 +11899,8 @@
                     errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
                     Slog.w(TAG, errorMsg);
                 }
-            } else if (throwable instanceof PackageParserException) {
-                PackageParserException e = (PackageParserException)
-                        throwable;
+            } else if (throwable instanceof PackageManagerException) {
+                PackageManagerException e = (PackageManagerException) throwable;
                 errorCode = e.error;
                 errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
                 Slog.w(TAG, errorMsg);
@@ -11888,14 +11940,14 @@
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
-            if (ps.signatures.mSigningDetails.signatures != null
-                    && ps.signatures.mSigningDetails.signatures.length != 0
-                    && ps.signatures.mSigningDetails.signatureSchemeVersion
+            if (ps.signatures.mSigningDetails.getSignatures() != null
+                    && ps.signatures.mSigningDetails.getSignatures().length != 0
+                    && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
                             != SignatureSchemeVersion.UNKNOWN) {
                 // Optimization: reuse the existing cached signing data
                 // if the package appears to be unchanged.
                 parsedPackage.setSigningDetails(
-                        new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
+                        new SigningDetails(ps.signatures.mSigningDetails));
                 return;
             }
 
@@ -11908,10 +11960,14 @@
 
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
-            parsedPackage.setSigningDetails(
-                    ParsingPackageUtils.getSigningDetails(parsedPackage, skipVerify));
-        } catch (PackageParserException e) {
-            throw PackageManagerException.from(e);
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+                    input, parsedPackage, skipVerify);
+            if (result.isError()) {
+                throw new PackageManagerException(
+                        result.getErrorCode(), result.getErrorMessage(), result.getException());
+            }
+            parsedPackage.setSigningDetails(result.getResult());
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -11968,8 +12024,6 @@
         final ParsedPackage parsedPackage;
         try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
             parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
-        } catch (PackageParserException e) {
-            throw PackageManagerException.from(e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -12199,10 +12253,10 @@
 
             if (!parsedPackage.getSigningDetails()
                     .checkCapability(pkgSetting.signatures.mSigningDetails,
-                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+                    SigningDetails.CertCapabilities.INSTALLED_DATA)
                             && !pkgSetting.signatures.mSigningDetails.checkCapability(
                                     parsedPackage.getSigningDetails(),
-                                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+                                    SigningDetails.CertCapabilities.ROLLBACK)) {
                 logCriticalInfo(Log.WARN,
                         "System package signature mismatch;"
                         + " name: " + pkgSetting.name);
@@ -12438,6 +12492,7 @@
             }
         }
         builder.append(" to access user ");
+        builder.append(userId);
         builder.append(".");
         return builder.toString();
     }
@@ -13503,9 +13558,9 @@
                         // For apps targeting O MR1 we require explicit enumeration of all certs.
                         final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
                                 ? PackageUtils.computeSignaturesSha256Digests(
-                                libPkg.signatures)
+                                libPkg.getSignatures())
                                 : PackageUtils.computeSignaturesSha256Digests(
-                                        new Signature[]{libPkg.signatures[0]});
+                                        new Signature[]{libPkg.getSignatures()[0]});
 
                         // Take a shortcut if sizes don't match. Note that if an app doesn't
                         // target O we don't parse the "additional-certificate" tags similarly
@@ -13846,8 +13901,9 @@
                 // priv-apps.
                 synchronized (mLock) {
                     PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
-                    if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
-                            pkg.getSigningDetails().signatures)
+                    if ((compareSignatures(
+                            platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+                            pkg.getSigningDetails().getSignatures())
                             != PackageManager.SIGNATURE_MATCH)) {
                         scanFlags |= SCAN_AS_PRIVILEGED;
                     }
@@ -14686,8 +14742,8 @@
         parsedPackage.setSignedWithPlatformKey(
                 (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
                         || (platformPkg != null && compareSignatures(
-                        platformPkg.getSigningDetails().signatures,
-                        parsedPackage.getSigningDetails().signatures
+                        platformPkg.getSigningDetails().getSignatures(),
+                        parsedPackage.getSigningDetails().getSignatures()
                 ) == PackageManager.SIGNATURE_MATCH))
         );
 
@@ -14983,7 +15039,7 @@
                     // Exempt SharedUsers signed with the platform key.
                     PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
                     if (!comparePackageSignatures(platformPkgSetting,
-                            pkg.getSigningDetails().signatures)) {
+                            pkg.getSigningDetails().getSignatures())) {
                         throw new PackageManagerException("Apps that share a user with a " +
                                 "privileged app must themselves be marked as privileged. " +
                                 pkg.getPackageName() + " shares privileged user " +
@@ -15031,7 +15087,7 @@
                         final PackageSetting platformPkgSetting =
                                 mSettings.getPackageLPr("android");
                         if (!comparePackageSignatures(platformPkgSetting,
-                                pkg.getSigningDetails().signatures)) {
+                                pkg.getSigningDetails().getSignatures())) {
                             throw new PackageManagerException("Overlay "
                                     + pkg.getPackageName()
                                     + " must target Q or later, "
@@ -15051,7 +15107,7 @@
                                 mSettings.getPackageLPr(pkg.getOverlayTarget());
                         if (targetPkgSetting != null) {
                             if (!comparePackageSignatures(targetPkgSetting,
-                                    pkg.getSigningDetails().signatures)) {
+                                    pkg.getSigningDetails().getSignatures())) {
                                 // check reference signature
                                 if (mOverlayConfigSignaturePackage == null) {
                                     throw new PackageManagerException("Overlay "
@@ -15063,7 +15119,7 @@
                                 final PackageSetting refPkgSetting =
                                         mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
                                 if (!comparePackageSignatures(refPkgSetting,
-                                        pkg.getSigningDetails().signatures)) {
+                                        pkg.getSigningDetails().getSignatures())) {
                                     throw new PackageManagerException("Overlay "
                                             + pkg.getPackageName() + " signed with a different "
                                             + "certificate than both the reference package and "
@@ -15082,7 +15138,8 @@
                 int minSignatureSchemeVersion =
                         ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                                 pkg.getTargetSdkVersion());
-                if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
+                if (pkg.getSigningDetails().getSignatureSchemeVersion()
+                        < minSignatureSchemeVersion) {
                     throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                             "No signature found in package of version " + minSignatureSchemeVersion
                                     + " or newer for package " + pkg.getPackageName());
@@ -16892,7 +16949,7 @@
             final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
             if (pkg == null) {
                 return -1;
-            } else if (pkg.getSigningDetails().signatures.length != 1) {
+            } else if (pkg.getSigningDetails().getSignatures().length != 1) {
                 Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                         + " has more than one signature; ignoring");
                 return -1;
@@ -16906,7 +16963,7 @@
 
             final byte[] expectedPublicKey;
             try {
-                final Signature verifierSig = pkg.getSigningDetails().signatures[0];
+                final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
                 final PublicKey publicKey = verifierSig.getPublicKey();
                 expectedPublicKey = publicKey.getEncoded();
             } catch (CertificateException e) {
@@ -17170,9 +17227,10 @@
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     callerSignature =
-                            ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+                            ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
                 } else if (obj instanceof PackageSetting) {
-                    callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
+                    callerSignature =
+                            ((PackageSetting) obj).signatures.mSigningDetails.getSignatures();
                 } else {
                     throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
                 }
@@ -17184,7 +17242,7 @@
             // not signed with the same cert as the caller.
             if (installerPackageSetting != null) {
                 if (compareSignatures(callerSignature,
-                        installerPackageSetting.signatures.mSigningDetails.signatures)
+                        installerPackageSetting.signatures.mSigningDetails.getSignatures())
                         != PackageManager.SIGNATURE_MATCH) {
                     throw new SecurityException(
                             "Caller does not have same cert as new installer package "
@@ -17201,7 +17259,7 @@
 
             if (targetInstallerPkgSetting != null) {
                 if (compareSignatures(callerSignature,
-                        targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
+                        targetInstallerPkgSetting.signatures.mSigningDetails.getSignatures())
                         != PackageManager.SIGNATURE_MATCH) {
                     throw new SecurityException(
                             "Caller does not have same cert as old installer package "
@@ -17770,7 +17828,7 @@
         final String[] grantedRuntimePermissions;
         final List<String> whitelistedRestrictedPermissions;
         final int autoRevokePermissionsMode;
-        final PackageParser.SigningDetails signingDetails;
+        final SigningDetails signingDetails;
         final int installReason;
         final int mInstallScenario;
         @Nullable MultiPackageInstallParams mParentInstallParams;
@@ -17794,7 +17852,7 @@
             this.grantedRuntimePermissions = null;
             this.whitelistedRestrictedPermissions = null;
             this.autoRevokePermissionsMode = MODE_DEFAULT;
-            this.signingDetails = PackageParser.SigningDetails.UNKNOWN;
+            this.signingDetails = SigningDetails.UNKNOWN;
             this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
             this.mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
             this.forceQueryableOverride = false;
@@ -17983,8 +18041,9 @@
             // 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(
+                Pair<Integer, String> ret = verifyReplacingVersionCode(
                         pkgLite, requiredInstalledVersionCode, installFlags);
+                mRet = ret.first;
                 if (mRet != INSTALL_SUCCEEDED) {
                     return;
                 }
@@ -18065,17 +18124,20 @@
                 return;
             }
             int completeStatus = PackageManager.INSTALL_SUCCEEDED;
-            for (Integer status : mVerificationState.values()) {
+            String errorMsg = null;
+            for (VerificationParams params : mVerificationState.keySet()) {
+                int status = params.mRet;
                 if (status == PackageManager.INSTALL_UNKNOWN) {
                     return;
                 } else if (status != PackageManager.INSTALL_SUCCEEDED) {
                     completeStatus = status;
+                    errorMsg = params.mErrorMessage;
                     break;
                 }
             }
             try {
                 mObserver.onPackageInstalled(null, completeStatus,
-                        "Package Verification Result", new Bundle());
+                        errorMsg, new Bundle());
             } catch (RemoteException e) {
                 Slog.i(TAG, "Observer no longer exists.");
             }
@@ -18089,7 +18151,7 @@
         @NonNull final InstallSource installSource;
         final String packageAbiOverride;
         final VerificationInfo verificationInfo;
-        final PackageParser.SigningDetails signingDetails;
+        final SigningDetails signingDetails;
         @Nullable MultiPackageVerificationParams mParentVerificationParams;
         final long requiredInstalledVersionCode;
         final int mDataLoaderType;
@@ -18098,7 +18160,8 @@
         private boolean mWaitForVerificationToComplete;
         private boolean mWaitForIntegrityVerificationToComplete;
         private boolean mWaitForEnableRollbackToComplete;
-        private int mRet;
+        private int mRet = PackageManager.INSTALL_SUCCEEDED;
+        private String mErrorMessage = null;
 
         final PackageLite mPackageLite;
 
@@ -18132,17 +18195,12 @@
         }
 
         public void handleStartCopy() {
-            if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
-                // Apex packages get verified in StagingManager currently.
-                // TODO(b/136257624): Move apex verification logic out of StagingManager
-                mRet = INSTALL_SUCCEEDED;
-                return;
-            }
-
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
 
-            mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
+            Pair<Integer, String> ret = verifyReplacingVersionCode(
+                    pkgLite, requiredInstalledVersionCode, installFlags);
+            setReturnCode(ret.first, ret.second);
             if (mRet != INSTALL_SUCCEEDED) {
                 return;
             }
@@ -18150,7 +18208,10 @@
             // Perform package verification and enable rollback (unless we are simply moving the
             // package).
             if (!origin.existing) {
-                sendApkVerificationRequest(pkgLite);
+                if ((installFlags & PackageManager.INSTALL_APEX) == 0) {
+                    // TODO(b/182426975): treat APEX as APK when APK verification is concerned
+                    sendApkVerificationRequest(pkgLite);
+                }
                 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                     sendEnableRollbackRequest();
                 }
@@ -18165,7 +18226,7 @@
             mPendingVerification.append(verificationId, verificationState);
 
             sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
-            mRet = sendPackageVerificationRequest(
+            sendPackageVerificationRequest(
                     verificationId, pkgLite, verificationState);
 
             // If both verifications are skipped, we should remove the state.
@@ -18197,28 +18258,23 @@
             // it will not miss the broadcast.
             enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
 
-            mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
-                    android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
-                    new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            // the duration to wait for rollback to be enabled, in millis
-                            long rollbackTimeout = DeviceConfig.getLong(
-                                    DeviceConfig.NAMESPACE_ROLLBACK,
-                                    PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
-                                    DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
-                            if (rollbackTimeout < 0) {
-                                rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
-                            }
-                            final Message msg = mHandler.obtainMessage(
-                                    ENABLE_ROLLBACK_TIMEOUT);
-                            msg.arg1 = enableRollbackToken;
-                            msg.arg2 = mSessionId;
-                            mHandler.sendMessageDelayed(msg, rollbackTimeout);
-                        }
-                    }, null, 0, null, null);
+            mContext.sendBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
+                    android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
 
             mWaitForEnableRollbackToComplete = true;
+
+            // the duration to wait for rollback to be enabled, in millis
+            long rollbackTimeout = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_ROLLBACK,
+                    PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
+                    DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
+            if (rollbackTimeout < 0) {
+                rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
+            }
+            final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_TIMEOUT);
+            msg.arg1 = enableRollbackToken;
+            msg.arg2 = mSessionId;
+            mHandler.sendMessageDelayed(msg, rollbackTimeout);
         }
 
         /**
@@ -18282,14 +18338,12 @@
         }
 
         /**
-         * Send a request to verifier(s) to verify the package if necessary, and return
-         * {@link PackageManager#INSTALL_SUCCEEDED} if succeeded.
+         * Send a request to verifier(s) to verify the package if necessary.
          */
-        int sendPackageVerificationRequest(
+        void sendPackageVerificationRequest(
                 int verificationId,
                 PackageInfoLite pkgLite,
                 PackageVerificationState verificationState) {
-            int ret = INSTALL_SUCCEEDED;
 
             // TODO: http://b/22976637
             // Apps installed for "all" users use the device owner to verify the app
@@ -18311,7 +18365,7 @@
             final boolean isVerificationEnabled = isVerificationEnabled(
                     pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
             final boolean isV4Signed =
-                    (signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+                    (signingDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
             final boolean isIncrementalInstall =
                     (mDataLoaderType == DataLoaderType.INCREMENTAL);
             // NOTE: We purposefully skip verification for only incremental installs when there's
@@ -18372,8 +18426,9 @@
                 if (sufficientVerifiers != null) {
                     final int n = sufficientVerifiers.size();
                     if (n == 0) {
-                        Slog.i(TAG, "Additional verifiers required, but none installed.");
-                        ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+                        String errorMsg = "Additional verifiers required, but none installed.";
+                        Slog.i(TAG, errorMsg);
+                        setReturnCode(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
                     } else {
                         for (int i = 0; i < n; i++) {
                             final ComponentName verifierComponent = sufficientVerifiers.get(i);
@@ -18431,7 +18486,6 @@
                 verificationState.setVerifierResponse(
                         requiredUid, PackageManager.VERIFICATION_ALLOW);
             }
-            return ret;
         }
 
         void populateInstallerExtras(Intent intent) {
@@ -18458,11 +18512,12 @@
             }
         }
 
-        void setReturnCode(int ret) {
+        void setReturnCode(int ret, String message) {
             if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                 // Only update mRet if it was previously INSTALL_SUCCEEDED to
                 // ensure we do not overwrite any previous failure results.
                 mRet = ret;
+                mErrorMessage = message;
             }
         }
 
@@ -18498,7 +18553,7 @@
                 mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
             } else {
                 try {
-                    observer.onPackageInstalled(null, mRet, "Package Verification Result",
+                    observer.onPackageInstalled(null, mRet, mErrorMessage,
                             new Bundle());
                 } catch (RemoteException e) {
                     Slog.i(TAG, "Observer no longer exists.");
@@ -18542,7 +18597,7 @@
         /** If non-null, drop an async trace when the install completes */
         final String traceMethod;
         final int traceCookie;
-        final PackageParser.SigningDetails signingDetails;
+        final SigningDetails signingDetails;
         final int installReason;
         final int mInstallScenario;
         final boolean forceQueryableOverride;
@@ -18680,7 +18735,7 @@
         FileInstallArgs(String codePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                     null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
-                    PackageParser.SigningDetails.UNKNOWN,
+                    SigningDetails.UNKNOWN,
                     PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
                     false, DataLoaderType.NONE);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
@@ -18781,12 +18836,12 @@
 
             // Reflect the rename in scanned details
             try {
-                parsedPackage.setCodePath(afterCodeFile.getCanonicalPath());
+                parsedPackage.setPath(afterCodeFile.getCanonicalPath());
             } catch (IOException e) {
                 Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
                 return false;
             }
-            parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+            parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile,
                     afterCodeFile, parsedPackage.getBaseApkPath()));
             parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
                     afterCodeFile, parsedPackage.getSplitCodePaths()));
@@ -18996,12 +19051,6 @@
             Slog.w(TAG, msg);
         }
 
-        public void setError(String msg, PackageParserException e) {
-            setReturnCode(e.error);
-            setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
-            Slog.w(TAG, msg, e);
-        }
-
         public void setError(String msg, PackageManagerException e) {
             returnCode = e.error;
             setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
@@ -19531,11 +19580,11 @@
                     // the signatures on the first package scanned for the shared user (i.e. if the
                     // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
                     if (signatureCheckPs.sharedUser != null) {
-                        final Signature[] sharedUserSignatures =
-                                signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+                        final Signature[] sharedUserSignatures = signatureCheckPs.sharedUser
+                                .signatures.mSigningDetails.getSignatures();
                         if (signatureCheckPs.sharedUser.signaturesChanged != null
                                 && compareSignatures(sharedUserSignatures,
-                                parsedPackage.getSigningDetails().signatures)
+                                parsedPackage.getSigningDetails().getSignatures())
                                         != PackageManager.SIGNATURE_MATCH) {
                             if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
                                 // Mismatched signatures is an error and silently skipping system
@@ -19939,7 +19988,7 @@
                     if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
                         continue;
                     }
-                    if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+                    if (args.signingDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
                         continue;
                     }
                     // For incremental installs, we bypass the verifier prior to install. Now
@@ -20326,9 +20375,7 @@
         }
 
         PrepareFailure(String message, Exception e) {
-            super(e instanceof PackageParserException
-                    ? ((PackageParserException) e).error
-                    : ((PackageManagerException) e).error,
+            super(((PackageManagerException) e).error,
                     ExceptionUtils.getCompleteMessage(message, e));
         }
 
@@ -20365,11 +20412,11 @@
             // older certificate with which the current is ok with sharing permissions
             if (sourceSigningDetails.checkCapability(
                     parsedPackage.getSigningDetails(),
-                    PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                    SigningDetails.CertCapabilities.PERMISSION)) {
                 return true;
             } else if (parsedPackage.getSigningDetails().checkCapability(
                     sourceSigningDetails,
-                    PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                    SigningDetails.CertCapabilities.PERMISSION)) {
                 // the scanned package checks out, has signing certificate rotation
                 // history, and is newer; bring it over
                 synchronized (mLock) {
@@ -20441,7 +20488,7 @@
         try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
             parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
             AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             throw new PrepareFailure("Failed parse during installPackageLI", e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -20482,19 +20529,21 @@
             }
         }
 
-        try {
-            // either use what we've been given or parse directly from the APK
-            if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
-                parsedPackage.setSigningDetails(args.signingDetails);
-            } else {
-                parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(
-                        parsedPackage, false /* skipVerify */));
+        // either use what we've been given or parse directly from the APK
+        if (args.signingDetails != SigningDetails.UNKNOWN) {
+            parsedPackage.setSigningDetails(args.signingDetails);
+        } else {
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+                    input, parsedPackage, false /*skipVerify*/);
+            if (result.isError()) {
+                throw new PrepareFailure("Failed collect during installPackageLI",
+                        result.getException());
             }
-        } catch (PackageParserException e) {
-            throw new PrepareFailure("Failed collect during installPackageLI", e);
+            parsedPackage.setSigningDetails(result.getResult());
         }
 
-        if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
+        if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
                 < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
             Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                     + " is not signed with at least APK Signature Scheme v2");
@@ -21859,6 +21908,8 @@
                     }
                     clearPackagePreferredActivitiesLPw(
                             deletedPs.name, changedUsers, UserHandle.USER_ALL);
+
+                    mSettings.removeRenamedPackageLPw(deletedPs.realName);
                 }
                 if (changedUsers.size() > 0) {
                     updateDefaultHomeNotLocked(changedUsers);
@@ -23581,25 +23632,6 @@
                 getPackageFromComponentString(R.string.config_defaultRotationResolverService));
     }
 
-    private @Nullable String getDocumenterPackageName() {
-        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
-        intent.addCategory(Intent.CATEGORY_OPENABLE);
-        intent.setType("*/*");
-        final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
-
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
-                        | MATCH_DISABLED_COMPONENTS,
-                UserHandle.myUserId());
-        if (matches.size() == 1) {
-            return matches.get(0).getComponentInfo().packageName;
-        } else {
-            Slog.e(TAG, "There should probably be exactly one documenter; found "
-                    + matches.size() + ": matches=" + matches);
-            return null;
-        }
-    }
-
     @Nullable
     private String getDeviceConfiguratorPackageName() {
         return ensureSystemPackageName(mContext.getString(
@@ -23667,10 +23699,10 @@
         final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
         if (androidPkg != null) {
             final SigningDetails signingDetail = androidPkg.getSigningDetails();
-            if (signingDetail != null && signingDetail.signatures != null) {
+            if (signingDetail != null && signingDetail.getSignatures() != null) {
                 try {
                     final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
-                    for (Signature signature : signingDetail.signatures) {
+                    for (Signature signature : signingDetail.getSignatures()) {
                         if (TextUtils.equals(predefinedSignature,
                                 HexEncoding.encodeToString(msgDigest.digest(
                                         signature.toByteArray()), false))) {
@@ -23860,7 +23892,6 @@
             throw new IllegalArgumentException("Invalid new component state: "
                     + newState);
         }
-        PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
         final int permission;
         if (callingUid == Process.SYSTEM_UID) {
@@ -23874,55 +23905,43 @@
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
         boolean sendNow = false;
         boolean isApp = (className == null);
-        final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
         String componentName = isApp ? packageName : className;
         ArrayList<String> components;
 
+        final boolean isCallerTargetApp = ArrayUtils.contains(
+                getPackagesForUid(callingUid), packageName);
+        final PackageSetting pkgSetting;
         // reader
         synchronized (mLock) {
             pkgSetting = mSettings.getPackageLPr(packageName);
-            if (pkgSetting == null) {
-                if (!isCallerInstantApp) {
-                    if (className == null) {
-                        throw new IllegalArgumentException("Unknown package: " + packageName);
-                    }
-                    throw new IllegalArgumentException(
-                            "Unknown component: " + packageName + "/" + className);
-                } else {
-                    // throw SecurityException to prevent leaking package information
+            // Limit who can change which apps
+            if (!isCallerTargetApp) {
+                // Don't allow apps that don't have permission to modify other apps
+                if (!allowedByPermission
+                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
                     throw new SecurityException(
                             "Attempt to change component state; "
-                            + "pid=" + Binder.getCallingPid()
-                            + ", uid=" + callingUid
-                            + (className == null
-                                    ? ", package=" + packageName
-                                    : ", component=" + packageName + "/" + className));
+                                    + "pid=" + Binder.getCallingPid()
+                                    + ", uid=" + callingUid
+                                    + (className == null
+                                            ? ", package=" + packageName
+                                            : ", component=" + packageName + "/" + className));
                 }
+                // Don't allow changing protected packages.
+                if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                    throw new SecurityException(
+                            "Cannot disable a protected package: " + packageName);
+                }
+            }
+            if (pkgSetting == null) {
+                if (className == null) {
+                    throw new IllegalArgumentException("Unknown package: " + packageName);
+                }
+                throw new IllegalArgumentException(
+                        "Unknown component: " + packageName + "/" + className);
             }
         }
 
-        // Limit who can change which apps
-        if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
-            // Don't allow apps that don't have permission to modify other apps
-            final boolean filterApp;
-            synchronized (mLock) {
-                filterApp = (!allowedByPermission
-                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
-            }
-            if (filterApp) {
-                throw new SecurityException(
-                        "Attempt to change component state; "
-                                + "pid=" + Binder.getCallingPid()
-                                + ", uid=" + callingUid
-                                + (className == null
-                                ? ", package=" + packageName
-                                        : ", component=" + packageName + "/" + className));
-            }
-            // Don't allow changing protected packages.
-            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-                throw new SecurityException("Cannot disable a protected package: " + packageName);
-            }
-        }
         // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
         // app details activity
         if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
@@ -24460,7 +24479,7 @@
         boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                 mContext.getContentResolver(),
                 android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
-        PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
+        ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);
 
         if (DEBUG_SETTINGS) {
             Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
@@ -26714,8 +26733,13 @@
         }
     }
 
-    private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
+    private Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
             long requiredInstalledVersionCode, int installFlags) {
+        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+            return verifyReplacingVersionCodeForApex(
+                    pkgLite, requiredInstalledVersionCode, installFlags);
+        }
+
         String packageName = pkgLite.packageName;
         synchronized (mLock) {
             // Package which currently owns the data that the new package will own if installed.
@@ -26732,18 +26756,22 @@
 
             if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
                 if (dataOwnerPkg == null) {
-                    Slog.w(TAG, "Required installed version code was "
+                    String errorMsg = "Required installed version code was "
                             + requiredInstalledVersionCode
-                            + " but package is not installed");
-                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                            + " but package is not installed";
+                    Slog.w(TAG, errorMsg);
+                    return Pair.create(
+                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
                 }
 
                 if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
-                    Slog.w(TAG, "Required installed version code was "
+                    String errorMsg = "Required installed version code was "
                             + requiredInstalledVersionCode
                             + " but actual installed version is "
-                            + dataOwnerPkg.getLongVersionCode());
-                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                            + dataOwnerPkg.getLongVersionCode();
+                    Slog.w(TAG, errorMsg);
+                    return Pair.create(
+                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
                 }
             }
 
@@ -26753,13 +26781,52 @@
                     try {
                         checkDowngrade(dataOwnerPkg, pkgLite);
                     } catch (PackageManagerException e) {
-                        Slog.w(TAG, "Downgrade detected: " + e.getMessage());
-                        return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+                        String errorMsg = "Downgrade detected: " + e.getMessage();
+                        Slog.w(TAG, errorMsg);
+                        return Pair.create(
+                                PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
                     }
                 }
             }
         }
-        return PackageManager.INSTALL_SUCCEEDED;
+        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+    }
+
+    private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+            long requiredInstalledVersionCode, int installFlags) {
+        String packageName = pkgLite.packageName;
+
+        final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
+                ApexManager.MATCH_ACTIVE_PACKAGE);
+        if (activePackage == null) {
+            String errorMsg = "Attempting to install new APEX package " + packageName;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
+        }
+
+        final long activeVersion = activePackage.getLongVersionCode();
+        if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+                && activeVersion != requiredInstalledVersionCode) {
+            String errorMsg = "Installed version of APEX package " + packageName
+                    + " does not match required. Active version: " + activeVersion
+                    + " required: " + requiredInstalledVersionCode;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+        }
+
+        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        final long newVersionCode = pkgLite.getLongVersionCode();
+        if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+                && newVersionCode < activeVersion) {
+            String errorMsg = "Downgrade of APEX package " + packageName
+                    + " is not allowed. Active version: " + activeVersion
+                    + " attempted: " + newVersionCode;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+        }
+
+        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
     }
 
     /**
@@ -27069,6 +27136,22 @@
         return mComputer.filterAppAccess(packageName, callingUid, userId);
     }
 
+    private boolean filterAppAccess(int uid, int callingUid) {
+        return mComputer.filterAppAccess(uid, callingUid);
+    }
+
+    private int[] getVisibilityAllowList(@NonNull String packageName, int userId) {
+        synchronized (mLock) {
+            final PackageSetting ps = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
+            if (ps == null) {
+                return null;
+            }
+            final SparseArray<int[]> visibilityAllowList = mAppsFilter.getVisibilityAllowList(ps,
+                    new int[]{userId}, mSettings.getPackagesLocked());
+            return visibilityAllowList != null ? visibilityAllowList.get(userId) : null;
+        }
+    }
+
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
@@ -27090,7 +27173,7 @@
             }
             return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
                     || mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
-                    PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+                    SigningDetails.CertCapabilities.PERMISSION);
         }
 
         @Override
@@ -27152,6 +27235,16 @@
         }
 
         @Override
+        public boolean filterAppAccess(int uid, int callingUid) {
+            return PackageManagerService.this.filterAppAccess(uid, callingUid);
+        }
+
+        @Nullable
+        public int[] getVisibilityAllowList(@NonNull String packageName, int userId) {
+            return PackageManagerService.this.getVisibilityAllowList(packageName, userId);
+        }
+
+        @Override
         public AndroidPackage getPackage(String packageName) {
             return PackageManagerService.this.getPackage(packageName);
         }
@@ -27207,8 +27300,7 @@
         }
 
         /**
-         * Only keep package names that refer to {@link PackageParser.Package#isSystem system}
-         * packages.
+         * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
          *
          * @param pkgNames The packages to filter
          *
@@ -27267,8 +27359,6 @@
                             mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
                 case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
                     return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
-                case PackageManagerInternal.PACKAGE_DOCUMENTER:
-                    return filterOnlySystemPackages(mDocumenterPackage);
                 case PackageManagerInternal.PACKAGE_CONFIGURATOR:
                     return filterOnlySystemPackages(mConfiguratorPackage);
                 case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
@@ -28952,6 +29042,56 @@
         }
     }
 
+    @Override
+    public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+            String featureId, int userId) throws RemoteException {
+        Objects.requireNonNull(packageName);
+        final int callingUid = Binder.getCallingUid();
+        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get launch intent sender for package");
+        final int packageUid = getPackageUid(callingPackage, 0 /* flags */, userId);
+        if (!UserHandle.isSameApp(callingUid, packageUid)) {
+            throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+                    + callingUid + " does not own package: " + callingPackage);
+        }
+
+        // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
+        // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
+        final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+        intentToResolve.addCategory(Intent.CATEGORY_INFO);
+        intentToResolve.setPackage(packageName);
+        String resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+        List<ResolveInfo> ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+                0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+                true /* resolveForStart */, false /* allowDynamicSplits */);
+        if (ris == null || ris.size() <= 0) {
+            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+            intentToResolve.setPackage(packageName);
+            resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+            ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+                    0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+                    true /* resolveForStart */, false /* allowDynamicSplits */);
+        }
+
+        final Intent intent = new Intent(intentToResolve);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // For the case of empty result, no component name is assigned into the intent. A
+        // non-launchable IntentSender which contains the failed intent is created. The
+        // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
+        if (ris != null && !ris.isEmpty()) {
+            intent.setClassName(ris.get(0).activityInfo.packageName,
+                    ris.get(0).activityInfo.name);
+        }
+        final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+                ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                featureId, null /* token */, null /* resultWho */,
+                1 /* requestCode */, new Intent[] { intent },
+                resolvedType != null ? new String[] { resolvedType } : null,
+                PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
+        return new IntentSender(target);
+    }
+
     private static class TempUserState {
         public final int enabledState;
         @Nullable
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8b5abf3..5fdb57d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,9 +34,9 @@
 import android.content.Intent;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseResult;
@@ -499,9 +499,9 @@
      */
     public static boolean comparePackageSignatures(PackageSetting pkgSetting,
             Signature[] signatures) {
-        return pkgSetting.signatures.mSigningDetails
-                == PackageParser.SigningDetails.UNKNOWN
-                || compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, signatures)
+        final SigningDetails signingDetails = pkgSetting.signatures.mSigningDetails;
+        return signingDetails == SigningDetails.UNKNOWN
+                || compareSignatures(signingDetails.getSignatures(), signatures)
                 == PackageManager.SIGNATURE_MATCH;
     }
 
@@ -512,13 +512,13 @@
      * system upgrade) and {@code scannedSigs} will be in the newer format.
      */
     private static boolean matchSignaturesCompat(String packageName,
-            PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
+            PackageSignatures packageSignatures, SigningDetails parsedSignatures) {
         ArraySet<Signature> existingSet = new ArraySet<Signature>();
-        for (Signature sig : packageSignatures.mSigningDetails.signatures) {
+        for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) {
             existingSet.add(sig);
         }
         ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
-        for (Signature sig : parsedSignatures.signatures) {
+        for (Signature sig : parsedSignatures.getSignatures()) {
             try {
                 Signature[] chainSignatures = sig.getChainSignatures();
                 for (Signature chainSig : chainSignatures) {
@@ -547,9 +547,9 @@
 
     private static boolean matchSignaturesRecover(
             String packageName,
-            PackageParser.SigningDetails existingSignatures,
-            PackageParser.SigningDetails parsedSignatures,
-            @PackageParser.SigningDetails.CertCapabilities int flags) {
+            SigningDetails existingSignatures,
+            SigningDetails parsedSignatures,
+            @SigningDetails.CertCapabilities int flags) {
         String msg = null;
         try {
             if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
@@ -576,10 +576,10 @@
             PackageSetting disabledPkgSetting) {
         if (pkgSetting.signatures.mSigningDetails.checkCapability(
                 disabledPkgSetting.signatures.mSigningDetails,
-                PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+                SigningDetails.CertCapabilities.INSTALLED_DATA)
                 || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
                 pkgSetting.signatures.mSigningDetails,
-                PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+                SigningDetails.CertCapabilities.ROLLBACK)) {
             return true;
         } else {
             logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
@@ -623,19 +623,19 @@
      * @throws PackageManagerException if the signatures did not match.
      */
     public static boolean verifySignatures(PackageSetting pkgSetting,
-            PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
+            PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
             boolean compareCompat, boolean compareRecover, boolean isRollback)
             throws PackageManagerException {
         final String packageName = pkgSetting.name;
         boolean compatMatch = false;
-        if (pkgSetting.signatures.mSigningDetails.signatures != null) {
+        if (pkgSetting.signatures.mSigningDetails.getSignatures() != null) {
             // Already existing package. Make sure signatures match
             boolean match = parsedSignatures.checkCapability(
                     pkgSetting.signatures.mSigningDetails,
-                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+                    SigningDetails.CertCapabilities.INSTALLED_DATA)
                             || pkgSetting.signatures.mSigningDetails.checkCapability(
                                     parsedSignatures,
-                                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+                                    SigningDetails.CertCapabilities.ROLLBACK);
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
                         parsedSignatures);
@@ -646,12 +646,12 @@
                         packageName,
                         pkgSetting.signatures.mSigningDetails,
                         parsedSignatures,
-                        PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+                        SigningDetails.CertCapabilities.INSTALLED_DATA)
                                 || matchSignaturesRecover(
                                         packageName,
                                         parsedSignatures,
                                         pkgSetting.signatures.mSigningDetails,
-                                        PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+                                        SigningDetails.CertCapabilities.ROLLBACK);
             }
 
             if (!match && isApkVerificationForced(disabledPkgSetting)) {
@@ -674,7 +674,7 @@
         // Check for shared user signatures
         if (pkgSetting.getSharedUser() != null
                 && pkgSetting.getSharedUser().signatures.mSigningDetails
-                        != PackageParser.SigningDetails.UNKNOWN) {
+                        != SigningDetails.UNKNOWN) {
 
             // Already existing package. Make sure signatures match.  In case of signing certificate
             // rotation, the packages with newer certs need to be ok with being sharedUserId with
@@ -684,10 +684,10 @@
             boolean match =
                     parsedSignatures.checkCapability(
                             pkgSetting.getSharedUser().signatures.mSigningDetails,
-                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+                            SigningDetails.CertCapabilities.SHARED_USER_ID)
                     || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
                             parsedSignatures,
-                            PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+                            SigningDetails.CertCapabilities.SHARED_USER_ID);
             // Special case: if the sharedUserId capability check failed it could be due to this
             // being the only package in the sharedUserId so far and the lineage being updated to
             // deny the sharedUserId capability of the previous key in the lineage.
@@ -704,11 +704,11 @@
                         matchSignaturesRecover(packageName,
                                 pkgSetting.getSharedUser().signatures.mSigningDetails,
                                 parsedSignatures,
-                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+                                SigningDetails.CertCapabilities.SHARED_USER_ID)
                         || matchSignaturesRecover(packageName,
                                 parsedSignatures,
                                 pkgSetting.getSharedUser().signatures.mSigningDetails,
-                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+                                SigningDetails.CertCapabilities.SHARED_USER_ID);
                 compatMatch |= match;
             }
             if (!match) {
@@ -729,13 +729,13 @@
                     if (packageName.equals(shUidPkgSetting.name)) {
                         continue;
                     }
-                    PackageParser.SigningDetails shUidSigningDetails =
+                    SigningDetails shUidSigningDetails =
                             shUidPkgSetting.getSigningDetails();
                     // The capability check only needs to be performed against the package if it is
                     // signed with a key that is in the lineage of the package being installed.
                     if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
                         if (!parsedSignatures.checkCapability(shUidSigningDetails,
-                                PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+                                SigningDetails.CertCapabilities.SHARED_USER_ID)) {
                             throw new PackageManagerException(
                                     INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                                     "Package " + packageName
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1aa80a9..4862021 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2644,14 +2644,25 @@
         }
     }
 
-    // pm remove-user [--set-ephemeral-if-in-use] USER_ID
+    // pm remove-user [--set-ephemeral-if-in-use][--wait] USER_ID
     public int runRemoveUser() throws RemoteException {
         int userId;
         String arg;
         boolean setEphemeralIfInUse = false;
+        boolean wait = false;
+
         while ((arg = getNextOption()) != null) {
-            if (arg.equals("--set-ephemeral-if-in-use")) {
-                setEphemeralIfInUse = true;
+            switch (arg) {
+                case "--set-ephemeral-if-in-use":
+                    setEphemeralIfInUse = true;
+                    break;
+                case "--wait": // fallthrough
+                case "-w":
+                    wait = true;
+                    break;
+                default:
+                    getErrPrintWriter().println("Error: unknown option: " + arg);
+                    return -1;
             }
         }
 
@@ -2666,18 +2677,64 @@
         if (setEphemeralIfInUse) {
             return removeUserOrSetEphemeral(um, userId);
         } else {
-            return removeUser(um, userId);
+            final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId);
+            if (success) {
+                getOutPrintWriter().println("Success: removed user");
+                return 0;
+            } else {
+                // Error message should already have been printed.
+                return 1;
+            }
         }
     }
 
-    private int removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException {
+    private boolean 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;
+            return true;
         } else {
             getErrPrintWriter().println("Error: couldn't remove user id " + userId);
-            return 1;
+            return false;
+        }
+    }
+
+    private boolean removeUserAndWait(IUserManager um, @UserIdInt int userId)
+            throws RemoteException {
+        Slog.i(TAG, "Removing (and waiting for completion) user " + userId);
+
+        final CountDownLatch waitLatch = new CountDownLatch(1);
+        final UserManagerInternal.UserLifecycleListener listener =
+                new UserManagerInternal.UserLifecycleListener() {
+                    @Override
+                    public void onUserRemoved(UserInfo user) {
+                        if (userId == user.id) {
+                            waitLatch.countDown();
+                        }
+                    }
+                };
+
+        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+        umi.addUserLifecycleListener(listener);
+
+        try {
+            if (um.removeUser(userId)) {
+                final boolean awaitSuccess = waitLatch.await(10, TimeUnit.MINUTES);
+                if (!awaitSuccess) {
+                    getErrPrintWriter().printf("Error: Remove user %d timed out\n", userId);
+                    return false;
+                }
+                // Success!
+                return true;
+            } else {
+                getErrPrintWriter().println("Error: couldn't remove user id " + userId);
+                return false;
+            }
+        } catch (InterruptedException e) {
+            getErrPrintWriter().printf("Error: Remove user %d wait interrupted: %s\n", userId, e);
+            Thread.currentThread().interrupt();
+            return false;
+        } finally {
+            umi.removeUserLifecycleListener(listener);
         }
     }
 
@@ -3827,13 +3884,14 @@
         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 [--set-ephemeral-if-in-use] USER_ID");
+        pw.println("  remove-user [--set-ephemeral-if-in-use | --wait] USER_ID");
         pw.println("    Remove the user with the given USER_IDENTIFIER, deleting all data");
         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("      --wait: Wait until user is removed. Ignored if set-ephemeral-if-in-use");
         pw.println("");
         pw.println("  set-user-restriction [--user USER_ID] RESTRICTION VALUE");
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bf82bd8..3d1a841 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -27,9 +27,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.PackageManager.UninstallReason;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.overlay.OverlayPaths;
 import android.os.PersistableBundle;
@@ -226,10 +226,10 @@
     }
 
     public Signature[] getSignatures() {
-        return signatures.mSigningDetails.signatures;
+        return signatures.mSigningDetails.getSignatures();
     }
 
-    public PackageParser.SigningDetails getSigningDetails() {
+    public SigningDetails getSigningDetails() {
         return signatures.mSigningDetails;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 394cdee..a1e5051 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -17,9 +17,9 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.util.Log;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -35,40 +35,41 @@
 
 class PackageSignatures {
 
-    @NonNull PackageParser.SigningDetails mSigningDetails;
+    @NonNull SigningDetails mSigningDetails;
 
     PackageSignatures(PackageSignatures orig) {
-        if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
-            mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+        if (orig != null && orig.mSigningDetails != SigningDetails.UNKNOWN) {
+            mSigningDetails = new SigningDetails(orig.mSigningDetails);
         } else {
-            mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+            mSigningDetails = SigningDetails.UNKNOWN;
         }
     }
 
-    PackageSignatures(PackageParser.SigningDetails signingDetails) {
+    PackageSignatures(SigningDetails signingDetails) {
         mSigningDetails = signingDetails;
     }
 
     PackageSignatures() {
-        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+        mSigningDetails = SigningDetails.UNKNOWN;
     }
 
     void writeXml(TypedXmlSerializer serializer, String tagName,
             ArrayList<Signature> writtenSignatures) throws IOException {
-        if (mSigningDetails.signatures == null) {
+        if (mSigningDetails.getSignatures() == null) {
             return;
         }
         serializer.startTag(null, tagName);
-        serializer.attributeInt(null, "count", mSigningDetails.signatures.length);
-        serializer.attributeInt(null, "schemeVersion", mSigningDetails.signatureSchemeVersion);
-        writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
+        serializer.attributeInt(null, "count", mSigningDetails.getSignatures().length);
+        serializer.attributeInt(null, "schemeVersion", mSigningDetails.getSignatureSchemeVersion());
+        writeCertsListXml(serializer, writtenSignatures, mSigningDetails.getSignatures(), false);
 
         // if we have past signer certificate information, write it out
-        if (mSigningDetails.pastSigningCertificates != null) {
+        if (mSigningDetails.getPastSigningCertificates() != null) {
             serializer.startTag(null, "pastSigs");
-            serializer.attributeInt(null, "count", mSigningDetails.pastSigningCertificates.length);
+            serializer.attributeInt(null, "count",
+                    mSigningDetails.getPastSigningCertificates().length);
             writeCertsListXml(serializer, writtenSignatures,
-                    mSigningDetails.pastSigningCertificates, true);
+                    mSigningDetails.getPastSigningCertificates(), true);
             serializer.endTag(null, "pastSigs");
         }
         serializer.endTag(null, tagName);
@@ -106,8 +107,7 @@
 
     void readXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures)
             throws IOException, XmlPullParserException {
-        PackageParser.SigningDetails.Builder builder =
-                new PackageParser.SigningDetails.Builder();
+        SigningDetails.Builder builder = new SigningDetails.Builder();
 
         final int count = parser.getAttributeInt(null, "count", -1);
         if (count == -1) {
@@ -142,13 +142,13 @@
             PackageManagerService.reportSettingsProblem(Log.WARN,
                     "Error in package manager settings: <sigs> "
                             + "unable to convert certificate(s) to public key(s).");
-            mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+            mSigningDetails = SigningDetails.UNKNOWN;
         }
     }
 
     private int readCertsListXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures,
             ArrayList<Signature> signatures, int count, boolean isPastSigs,
-            PackageParser.SigningDetails.Builder builder)
+            SigningDetails.Builder builder)
             throws IOException, XmlPullParserException {
         int pos = 0;
 
@@ -306,25 +306,25 @@
         buf.append("PackageSignatures{");
         buf.append(Integer.toHexString(System.identityHashCode(this)));
         buf.append(" version:");
-        buf.append(mSigningDetails.signatureSchemeVersion);
+        buf.append(mSigningDetails.getSignatureSchemeVersion());
         buf.append(", signatures:[");
-        if (mSigningDetails.signatures != null) {
-            for (int i = 0; i < mSigningDetails.signatures.length; i++) {
+        if (mSigningDetails.getSignatures() != null) {
+            for (int i = 0; i < mSigningDetails.getSignatures().length; i++) {
                 if (i > 0) buf.append(", ");
                 buf.append(Integer.toHexString(
-                        mSigningDetails.signatures[i].hashCode()));
+                        mSigningDetails.getSignatures()[i].hashCode()));
             }
         }
         buf.append("]");
         buf.append(", past signatures:[");
-        if (mSigningDetails.pastSigningCertificates != null) {
-            for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+        if (mSigningDetails.getPastSigningCertificates() != null) {
+            for (int i = 0; i < mSigningDetails.getPastSigningCertificates().length; i++) {
                 if (i > 0) buf.append(", ");
                 buf.append(Integer.toHexString(
-                        mSigningDetails.pastSigningCertificates[i].hashCode()));
+                        mSigningDetails.getPastSigningCertificates()[i].hashCode()));
                 buf.append(" flags: ");
-                buf.append(
-                        Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
+                buf.append(Integer.toHexString(
+                        mSigningDetails.getPastSigningCertificates()[i].getFlags()));
             }
         }
         buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 21334c0..5625884 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -18,7 +18,6 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import android.content.pm.PackageParser;
 import android.os.Process;
 import android.os.Trace;
 
@@ -33,7 +32,7 @@
 import java.util.concurrent.ExecutorService;
 
 /**
- * Helper class for parallel parsing of packages using {@link PackageParser}.
+ * Helper class for parallel parsing of packages using {@link PackageParser2}.
  * <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
  * At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
  */
@@ -125,7 +124,7 @@
 
     @VisibleForTesting
     protected ParsedPackage parsePackage(File scanFile, int parseFlags)
-            throws PackageParser.PackageParserException {
+            throws PackageManagerException {
         return mPackageParser.parsePackage(scanFile, parseFlags, true);
     }
 }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c5fbfba..e28540c 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,10 +17,11 @@
 package com.android.server.pm;
 
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.os.Environment;
 import android.util.Slog;
 import android.util.Xml;
@@ -79,20 +80,20 @@
 
     /**
      * Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
-     * Turning this change off for an app targeting the latest SDK is a no-op.
+     * Turning this change on for an app targeting the latest SDK or higher is a no-op.
      *
      * <p>Has no effect for apps using shared user id.
      *
      * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
      */
-    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+    @Disabled
     @ChangeId
     static final long SELINUX_LATEST_CHANGES = 143539591L;
 
     /**
      * This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
      * to R targetSdkVersion enforced changes without changing target SDK. Turning this change
-     * off for an app targeting S is a no-op.
+     * off for an app targeting {@code >= android.os.Build.VERSION_CODES.R} is a no-op.
      *
      * <p>Has no effect for apps using shared user id.
      *
@@ -364,7 +365,8 @@
         }
         final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
         if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
-            return android.os.Build.VERSION_CODES.S;
+            return Math.max(
+                    android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, pkg.getTargetSdkVersion());
         } else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
             return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
         }
@@ -577,7 +579,7 @@
         // Check for exact signature matches across all certs.
         Signature[] certs = mCerts.toArray(new Signature[0]);
         if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
-                && !Signature.areExactMatch(certs, pkg.getSigningDetails().signatures)) {
+                && !Signature.areExactMatch(certs, pkg.getSigningDetails().getSignatures())) {
 
             // certs aren't exact match, but the package may have rotated from the known system cert
             if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 26aebbc..3995cc6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -747,6 +747,15 @@
         mRenamedPackages.remove(pkgName);
     }
 
+    void pruneRenamedPackagesLPw() {
+        for (int i = mRenamedPackages.size() - 1; i >= 0; i--) {
+            PackageSetting ps = mPackages.get(mRenamedPackages.valueAt(i));
+            if (ps == null) {
+                mRenamedPackages.removeAt(i);
+            }
+        }
+    }
+
     /** Gets and optionally creates a new shared user id. */
     SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
             boolean create) throws PackageManagerException {
@@ -1169,12 +1178,13 @@
     // by that time.
     void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
         // Update signatures if needed.
-        if (p.signatures.mSigningDetails.signatures == null) {
+        if (p.signatures.mSigningDetails.getSignatures() == null) {
             p.signatures.mSigningDetails = pkg.getSigningDetails();
         }
         // If this app defines a shared user id initialize
         // the shared user signatures as well.
-        if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+        if (p.sharedUser != null
+                && p.sharedUser.signatures.mSigningDetails.getSignatures() == null) {
             p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
         }
         addPackageSettingLPw(p, p.sharedUser);
@@ -2966,6 +2976,17 @@
             mReadMessages.append("Error reading: " + e.toString());
             PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
             Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+        } finally {
+            if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
+                Slog.wtf(PackageManagerService.TAG,
+                        "No internal VersionInfo found in settings, using current.");
+                findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+            }
+            if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) {
+                Slog.wtf(PackageManagerService.TAG,
+                        "No external VersionInfo found in settings, using current.");
+                findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
+            }
         }
 
         // If the build is setup to drop runtime permissions
@@ -4490,7 +4511,7 @@
             pw.print(prefix); pw.print("  versionName="); pw.println(pkg.getVersionName());
             pw.print(prefix); pw.print("  usesNonSdkApi="); pw.println(pkg.isUsesNonSdkApi());
             pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, pkg); pw.println();
-            final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
+            final int apkSigningVersion = pkg.getSigningDetails().getSignatureSchemeVersion();
             pw.print(prefix); pw.print("  apkSigningVersion="); pw.println(apkSigningVersion);
             pw.print(prefix); pw.print("  applicationInfo=");
             pw.println(pkg.toAppInfoToString());
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index fcbf40e..945df5d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -86,6 +86,7 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
@@ -598,6 +599,7 @@
         if (DEBUG_PROCSTATE) {
             Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
         }
+        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleOnUidStateChanged");
         synchronized (mLock) {
             mUidState.put(uid, procState);
 
@@ -608,6 +610,7 @@
                 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     private boolean isProcessStateForeground(int processState) {
@@ -703,10 +706,12 @@
         // or anything.
         final long start = getStatStartTime();
         injectRunOnNewThread(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleUnlockUser");
             synchronized (mLock) {
                 logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
                 getUserShortcutsLocked(userId);
             }
+            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         });
     }
 
@@ -715,6 +720,7 @@
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, "handleStopUser: user=" + userId);
         }
+        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
         synchronized (mLock) {
             unloadUserLocked(userId);
 
@@ -722,6 +728,7 @@
                 mUnlockedUsers.put(userId, false);
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     @GuardedBy("mLock")
@@ -1192,6 +1199,7 @@
             return;
         }
         try {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
             synchronized (mLock) {
                 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
                     final int userId = mDirtyUserIds.get(i);
@@ -1205,6 +1213,8 @@
             }
         } catch (Exception e) {
             wtf("Exception in saveDirtyInfo", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index bdbcb27..9dcae3d 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,17 +28,17 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.Bundle;
@@ -82,6 +82,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
@@ -114,6 +115,8 @@
     @GuardedBy("mSuccessfulStagedSessionIds")
     private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
 
+    private final CompletableFuture<Void> mBootCompleted = new CompletableFuture<>();
+
     interface StagedSession {
         boolean isMultiPackage();
         boolean isApexSession();
@@ -138,25 +141,28 @@
         boolean hasParentSessionId();
         long getCommittedMillis();
         void abandon();
-        boolean notifyStartPreRebootVerification();
         void notifyEndPreRebootVerification();
         void verifySession();
     }
 
-    StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
-        this(context, packageParserSupplier, ApexManager.getInstance());
+    StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier, Looper looper) {
+        this(context, packageParserSupplier, ApexManager.getInstance(), looper);
     }
 
     @VisibleForTesting
     StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
             ApexManager apexManager) {
+        this(context, packageParserSupplier, apexManager, BackgroundThread.get().getLooper());
+    }
+
+    StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+            ApexManager apexManager, Looper looper) {
         mContext = context;
         mPackageParserSupplier = packageParserSupplier;
 
         mApexManager = apexManager;
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mPreRebootVerificationHandler = new PreRebootVerificationHandler(
-                BackgroundThread.get().getLooper());
+        mPreRebootVerificationHandler = new PreRebootVerificationHandler(looper);
 
         if (mFailureReasonFile.exists()) {
             try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
@@ -211,13 +217,15 @@
         int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                 newApexPkg.applicationInfo.targetSdkVersion);
 
-        final SigningDetails newSigningDetails;
-        try {
-            newSigningDetails = ApkSignatureVerifier.verify(apexPath, minSignatureScheme);
-        } catch (PackageParserException e) {
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
+                input.reset(), apexPath, minSignatureScheme);
+        if (newResult.isError()) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Failed to parse APEX package " + apexPath + " : " + e, e);
+                    "Failed to parse APEX package " + apexPath + " : "
+                            + newResult.getException(), newResult.getException());
         }
+        final SigningDetails newSigningDetails = newResult.getResult();
 
         // Get signing details of the existing package
         final PackageInfo existingApexPkg = mApexManager.getPackageInfo(packageName,
@@ -228,15 +236,15 @@
             throw new IllegalStateException("Unknown apex package " + packageName);
         }
 
-        final SigningDetails existingSigningDetails;
-        try {
-            existingSigningDetails = ApkSignatureVerifier.verify(
-                existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
-        } catch (PackageParserException e) {
+        final ParseResult<SigningDetails> existingResult = ApkSignatureVerifier.verify(
+                input.reset(), existingApexPkg.applicationInfo.sourceDir,
+                SignatureSchemeVersion.JAR);
+        if (existingResult.isError()) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
-                            + " : " + e, e);
+                            + " : " + existingResult.getException(), existingResult.getException());
         }
+        final SigningDetails existingSigningDetails = existingResult.getResult();
 
         // Verify signing details for upgrade
         if (newSigningDetails.checkCapability(existingSigningDetails,
@@ -292,19 +300,10 @@
                             SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                             "Unable to generate package info: " + apexInfo.modulePath);
                 }
-            } catch (PackageParserException e) {
+            } catch (PackageManagerException e) {
                 throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                         "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
             }
-            final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
-                    ApexManager.MATCH_ACTIVE_PACKAGE);
-            if (activePackage == null) {
-                Slog.w(TAG, "Attempting to install new APEX package " + packageInfo.packageName);
-                throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                        "It is forbidden to install new APEX packages.");
-            }
-            checkRequiredVersionCode(session, activePackage);
-            checkDowngrade(session, activePackage, packageInfo);
             result.add(packageInfo);
             apexPackageNames.add(packageInfo.packageName);
         }
@@ -327,40 +326,6 @@
                 "Could not find rollback id for commit session: " + sessionId);
     }
 
-    private void checkRequiredVersionCode(final StagedSession session,
-            final PackageInfo activePackage) throws PackageManagerException {
-        if (session.sessionParams().requiredInstalledVersionCode
-                == PackageManager.VERSION_CODE_HIGHEST) {
-            return;
-        }
-        final long activeVersion = activePackage.applicationInfo.longVersionCode;
-        if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
-            throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Installed version of APEX package " + activePackage.packageName
-                            + " does not match required. Active version: " + activeVersion
-                            + " required: " + session.sessionParams().requiredInstalledVersionCode);
-        }
-    }
-
-    private void checkDowngrade(final StagedSession session,
-            final PackageInfo activePackage, final PackageInfo newPackage)
-            throws PackageManagerException {
-        final long activeVersion = activePackage.applicationInfo.longVersionCode;
-        final long newVersionCode = newPackage.applicationInfo.longVersionCode;
-        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
-                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
-                session.sessionParams().installFlags, isAppDebuggable);
-        if (activeVersion > newVersionCode && !allowsDowngrade) {
-            throw new PackageManagerException(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Downgrade of APEX package " + newPackage.packageName
-                            + " is not allowed. Active version: " + activeVersion
-                            + " attempted: " + newVersionCode);
-        }
-    }
-
     // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
     private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
             boolean needsCheckpoint) {
@@ -690,6 +655,7 @@
      * </ul>
      * @throws PackageManagerException if session fails the check
      */
+    // TODO(b/192625695): Rename this method which checks rollbacks in addition to overlapping
     @VisibleForTesting
     void checkNonOverlappingWithStagedSessions(@NonNull StagedSession session)
             throws PackageManagerException {
@@ -723,13 +689,6 @@
                     continue;
                 }
 
-                if (stagedSession.getCommittedMillis() > session.getCommittedMillis()) {
-                    // Ignore sessions that are committed after the provided session. When there are
-                    // overlaps between sessions, we will fail the one committed later instead of
-                    // the earlier one.
-                    continue;
-                }
-
                 // From here on, stagedSession is a parent active staged session
 
                 // Check if session is one of the active sessions
@@ -754,15 +713,28 @@
                             "Session was failed by rollback session: " + session.sessionId());
                     Slog.i(TAG, "Session " + root.sessionId() + " is marked failed due to "
                             + "rollback session: " + session.sessionId());
+                } else if (!isRollback && isRollback(stagedSession)) {
+                    throw new PackageManagerException(
+                            SessionInfo.STAGED_SESSION_CONFLICT,
+                            "Session was failed by rollback session: " + stagedSession.sessionId());
                 } else if (stagedSession.sessionContains(
                         s -> s.getPackageName().equals(packageName))) {
-                    // New session cannot have same package name as one of the active sessions
-                    throw new PackageManagerException(
-                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                            "Package: " + session.getPackageName() + " in session: "
-                                    + session.sessionId()
-                                    + " has been staged already by session: "
-                                    + stagedSession.sessionId(), null);
+                    // Fail the session committed later when there are overlapping packages
+                    if (stagedSession.getCommittedMillis() < session.getCommittedMillis()) {
+                        throw new PackageManagerException(
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                                "Package: " + session.getPackageName() + " in session: "
+                                        + session.sessionId()
+                                        + " has been staged already by session: "
+                                        + stagedSession.sessionId(), null);
+                    } else {
+                        stagedSession.setSessionFailed(
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                                "Package: " + packageName + " in session: "
+                                        + stagedSession.sessionId()
+                                        + " has been staged already by session: "
+                                        + session.sessionId());
+                    }
                 }
 
                 // Staging multiple root sessions is not allowed if device doesn't support
@@ -890,7 +862,8 @@
             } else if (!session.isSessionReady()) {
                 // The framework got restarted before the pre-reboot verification could complete,
                 // restart the verification.
-                mPreRebootVerificationHandler.startPreRebootVerification(session);
+                Slog.i(TAG, "Restart verification for session=" + session.sessionId());
+                mBootCompleted.thenRun(() -> session.verifySession());
                 StagedSession session2 = sessions.set(j - 1, session);
                 sessions.set(i, session2);
                 j--;
@@ -1079,7 +1052,7 @@
 
     @VisibleForTesting
     void onBootCompletedBroadcastReceived() {
-        mPreRebootVerificationHandler.readyToStart();
+        mBootCompleted.complete(null);
         BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
     }
 
@@ -1120,24 +1093,7 @@
         return session;
     }
 
-    // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
-    //  verification logic is extracted out of StagingManager into PMS, we can remove
-    //  this.
-    void notifyVerificationComplete(StagedSession session) {
-        mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
-    }
-
-    // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
-    //  verification logic is extracted out of StagingManager into PMS, we can remove
-    //  this.
-    void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
-        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
-    }
-
     private final class PreRebootVerificationHandler extends Handler {
-        // Hold sessions before handler gets ready to do the verification.
-        private List<StagedSession> mPendingSessions;
-        private boolean mIsReady;
 
         PreRebootVerificationHandler(Looper looper) {
             super(looper);
@@ -1151,7 +1107,6 @@
          * <p><ul>
          *     <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
          *     <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
-         *     <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
          *     <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
          * </ul></p>
          *
@@ -1159,8 +1114,7 @@
          */
         private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
         private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
-        private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
-        private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1180,9 +1134,6 @@
                     case MSG_PRE_REBOOT_VERIFICATION_APEX:
                         handlePreRebootVerification_Apex(session, rollbackId);
                         break;
-                    case MSG_PRE_REBOOT_VERIFICATION_APK:
-                        handlePreRebootVerification_Apk(session);
-                        break;
                     case MSG_PRE_REBOOT_VERIFICATION_END:
                         handlePreRebootVerification_End(session);
                         break;
@@ -1195,35 +1146,15 @@
             }
         }
 
-        // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
-        private synchronized void readyToStart() {
-            mIsReady = true;
-            if (mPendingSessions != null) {
-                for (int i = 0; i < mPendingSessions.size(); i++) {
-                    StagedSession session = mPendingSessions.get(i);
-                    startPreRebootVerification(session);
-                }
-                mPendingSessions = null;
-            }
-        }
-
         // Method for starting the pre-reboot verification
         private synchronized void startPreRebootVerification(
                 @NonNull StagedSession session) {
-            if (!mIsReady) {
-                if (mPendingSessions == null) {
-                    mPendingSessions = new ArrayList<>();
-                }
-                mPendingSessions.add(session);
-                return;
-            }
-
-            if (session.notifyStartPreRebootVerification()) {
+            mBootCompleted.thenRun(() -> {
                 int sessionId = session.sessionId();
                 Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
                 obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
                         .sendToTarget();
-            }
+            });
         }
 
         private void onPreRebootVerificationFailure(StagedSession session,
@@ -1252,12 +1183,6 @@
 
         private void notifyPreRebootVerification_Apex_Complete(
                 @NonNull StagedSession session) {
-            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
-                    .sendToTarget();
-        }
-
-        private void notifyPreRebootVerification_Apk_Complete(
-                @NonNull StagedSession session) {
             obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
                     .sendToTarget();
         }
@@ -1345,19 +1270,6 @@
         }
 
         /**
-         * Pre-reboot verification state for apk files. Session is sent to
-         * {@link PackageManagerService} for verification and it notifies back the result via
-         * {@link #notifyPreRebootVerification_Apk_Complete}
-         */
-        private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
-            if (!session.containsApkSession()) {
-                notifyPreRebootVerification_Apk_Complete(session);
-                return;
-            }
-            session.verifySession();
-        }
-
-        /**
          * Pre-reboot verification state for wrapping up:
          * <p><ul>
          *     <li>enables rollback if required</li>
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 9182d81..cc5187e 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -1,6 +1,22 @@
 {
   "presubmit": [
     {
+      "name": "CtsAtomicInstallTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ]
+    },
+    {
+      "name": "CtsPackageInstallTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ]
+    },
+    {
       "name": "CtsUsesLibraryHostTestCases"
     },
     {
@@ -41,6 +57,53 @@
       ]
     },
     {
+      "name": "FrameworksServicesTests",
+      "file_patterns": ["(/|^)ShortcutService\\.java"],
+      "options": [
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest1"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest2"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest3"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest4"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest5"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest6"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest7"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest8"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest9"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest10"
+        },
+        {
+          "include-filter": "com.android.server.pm.ShortcutManagerTest11"
+        }
+      ]
+    },
+    {
+      "name": "CtsShortcutHostTestCases",
+      "file_patterns": ["(/|^)ShortcutService\\.java"]
+    },
+    {
+      "name": "CtsShortcutManagerTestCases",
+      "file_patterns": ["(/|^)ShortcutService\\.java"]
+    },
+    {
       "name": "CtsContentTestCases",
       "options": [
         {
@@ -79,6 +142,19 @@
       ]
     },
     {
+      "name": "CtsAppSecurityHostTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options": [
+        {
+          "include-filter": "android.appsecurity.cts.SplitTests"
+        }
+      ]
+    },
+    {
       "name": "FrameworksCoreTests",
       "options": [
         {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d4feb3a..190e7bc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -117,6 +117,7 @@
 import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
 import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -727,7 +728,7 @@
     }
 
     /* Prunes out any partially created or partially removed users. */
-    void cleanupPartialUsers() {
+    private void cleanupPartialUsers() {
         ArrayList<UserInfo> partials = new ArrayList<>();
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
@@ -755,7 +756,7 @@
      * Removes any pre-created users from the system. Should be invoked after OTAs, to ensure
      * pre-created users are not stale. New pre-created pool can be re-created after the update.
      */
-    void cleanupPreCreatedUsers() {
+    private void cleanupPreCreatedUsers() {
         final ArrayList<UserInfo> preCreatedUsers;
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
@@ -1213,6 +1214,10 @@
 
     private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
             @Nullable String callingPackage) {
+        Slogf.i(LOG_TAG,
+                "requestQuietModeEnabled called by package %s, with enableQuietMode %b.",
+                callingPackage,
+                enableQuietMode);
         UserData userData;
         synchronized (mUsersLock) {
             userData = getUserDataLU(userId);
@@ -2293,13 +2298,13 @@
 
     // Package private for the inner class.
     @GuardedBy("mRestrictionsLock")
-    void applyUserRestrictionsLR(@UserIdInt int userId) {
+    private void applyUserRestrictionsLR(@UserIdInt int userId) {
         updateUserRestrictionsInternalLR(null, userId);
     }
 
     @GuardedBy("mRestrictionsLock")
     // Package private for the inner class.
-    void applyUserRestrictionsForAllUsersLR() {
+    private void applyUserRestrictionsForAllUsersLR() {
         if (DBG) {
             debug("applyUserRestrictionsForAllUsersLR");
         }
@@ -2900,8 +2905,7 @@
     }
 
     @GuardedBy("mUsersLock")
-    @VisibleForTesting
-    void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
+    private void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
             @NonNull ArrayMap<String, UserTypeDetails> userTypes,
             final int formerUserTypeVersion,
             @NonNull Set<Integer> userIdsToWrite) {
@@ -3880,6 +3884,7 @@
     }
 
     /** Checks that the flags do not contain mutually exclusive types/properties. */
+    @VisibleForTesting
     static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
         // Mask to check that flags don't refer to multiple user types.
         final int userTypeFlagMask = UserInfo.FLAG_GUEST | UserInfo.FLAG_DEMO
@@ -4186,6 +4191,7 @@
                         return false;
                     }
 
+                    Slog.i(LOG_TAG, "Removing user " + userId);
                     addRemovingUserIdLocked(userId);
                 }
 
@@ -4308,8 +4314,8 @@
         }
     }
 
-    void finishRemoveUser(final @UserIdInt int userId) {
-        if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId);
+    private void finishRemoveUser(final @UserIdInt int userId) {
+        Slog.i(LOG_TAG, "finishRemoveUser " + userId);
 
         UserInfo user;
         synchronized (mUsersLock) {
@@ -4368,6 +4374,7 @@
     }
 
     private void removeUserState(final @UserIdInt int userId) {
+        Slog.i(LOG_TAG, "Removing user state of user " + userId);
         try {
             mContext.getSystemService(StorageManager.class).destroyUserKey(userId);
         } catch (IllegalStateException e) {
@@ -4814,8 +4821,15 @@
         final int userSerial = userInfo.serialNumber;
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("prepareUserData-" + userId);
         mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        t.traceEnd();
+
+        t.traceBegin("reconcileAppsData-" + userId);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
+        t.traceEnd();
     }
 
     /**
@@ -4984,7 +4998,7 @@
         (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
     }
 
-    int onShellCommand(Shell shell, String cmd) {
+    private int onShellCommand(Shell shell, String cmd) {
         if (cmd == null) {
             return shell.handleDefaultCommands(cmd);
         }
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 1859b4c..01bf634 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -608,6 +608,8 @@
     private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
     private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
     private static final int TRON_COMPILATION_REASON_CMDLINE = 22;
+    private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
+    private static final int TRON_COMPILATION_REASON_VDEX = 24;
 
     // The annotation to add as a suffix to the compilation reason when dexopt was
     // performed with dex metadata.
@@ -628,6 +630,8 @@
             case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
             case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
             case "shared" : return TRON_COMPILATION_REASON_SHARED;
+            case "prebuilt" : return TRON_COMPILATION_REASON_PREBUILT;
+            case "vdex" : return TRON_COMPILATION_REASON_VDEX;
             case "install-fast" :
                 return TRON_COMPILATION_REASON_INSTALL_FAST;
             case "install-bulk" :
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 0f67be7..fa9ed66 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -417,7 +417,7 @@
      * Returns true if the package is installed and not hidden, or if the caller
      * explicitly wanted all uninstalled and hidden packages as well.
      */
-    private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+    public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
             PackageSetting pkgSetting, PackageUserState state,
             @PackageManager.PackageInfoFlags int flags) {
         // Returns false if the package is hidden system app until installed.
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index e8be9b6..203356d 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
@@ -39,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -147,7 +147,7 @@
      */
     @AnyThread
     public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
-            throws PackageParserException {
+            throws PackageManagerException {
         if (useCaches && mCacher != null) {
             ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
             if (parsed != null) {
@@ -159,7 +159,7 @@
         ParseInput input = mSharedResult.get().reset();
         ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
         if (result.isError()) {
-            throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
+            throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
                     result.getException());
         }
 
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 471a4d3..0d2bcec 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -21,8 +21,8 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackageRead;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ParsedAttribution;
@@ -200,7 +200,7 @@
      * The signature data of all APKs in this package, which must be exactly the same across the
      * base and splits.
      */
-    PackageParser.SigningDetails getSigningDetails();
+    SigningDetails getSigningDetails();
 
     /**
      * TODO(b/135203078): Move split stuff to an inner data class
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 5ee612b..d40aada 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -21,7 +21,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.DexMetadataHelper;
@@ -31,12 +30,15 @@
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedProvider;
 import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.incremental.IncrementalManager;
 import android.text.TextUtils;
 
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
+import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.PackageSetting;
 
 import java.io.IOException;
@@ -120,15 +122,21 @@
     /**
      * Validate the dex metadata files installed for the given package.
      *
-     * @throws PackageParserException in case of errors.
+     * @throws PackageManagerException in case of errors.
      */
     public static void validatePackageDexMetadata(AndroidPackage pkg)
-            throws PackageParserException {
+            throws PackageManagerException {
         Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
         String packageName = pkg.getPackageName();
         long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
         for (String dexMetadata : apkToDexMetadataList) {
-            DexMetadataHelper.validateDexMetadataFile(dexMetadata, packageName, versionCode);
+            final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
+                    input.reset(), dexMetadata, packageName, versionCode);
+            if (result.isError()) {
+                throw new PackageManagerException(
+                        result.getErrorCode(), result.getErrorMessage(), result.getException());
+            }
         }
     }
 
@@ -196,6 +204,12 @@
             }
         }
 
+        if (pkg.getBackupAgentName() != null) {
+            if (Objects.equals(className, pkg.getBackupAgentName())) {
+                return true;
+            }
+        }
+
         return false;
     }
 
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 7fbe953..3eb4bde 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,7 +22,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageImpl;
 import android.content.pm.parsing.component.ParsedActivity;
@@ -252,7 +252,7 @@
     }
 
     @Override
-    public PackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+    public PackageImpl setSigningDetails(@Nullable SigningDetails value) {
         super.setSigningDetails(value);
         return this;
     }
@@ -306,8 +306,8 @@
     }
 
     @Override
-    public PackageImpl setCodePath(@NonNull String value) {
-        this.mPath = value;
+    public PackageImpl setPath(@NonNull String path) {
+        this.mPath = path;
         return this;
     }
 
@@ -381,8 +381,8 @@
     }
 
     @Override
-    public PackageImpl setBaseCodePath(@NonNull String baseCodePath) {
-        this.mBaseApkPath = TextUtils.safeIntern(baseCodePath);
+    public PackageImpl setBaseApkPath(@NonNull String baseApkPath) {
+        this.mBaseApkPath = TextUtils.safeIntern(baseApkPath);
         return this;
     }
 
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 657f32c..0051bab 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -17,7 +17,7 @@
 package com.android.server.pm.parsing.pkg;
 
 import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
 
 /**
  * Methods used for mutation after direct package parsing, mostly done inside
@@ -43,9 +43,9 @@
 
     ParsedPackage clearProtectedBroadcasts();
 
-    ParsedPackage setBaseCodePath(String baseCodePath);
+    ParsedPackage setBaseApkPath(String baseApkPath);
 
-    ParsedPackage setCodePath(String codePath);
+    ParsedPackage setPath(String path);
 
     ParsedPackage setNativeLibraryDir(String nativeLibraryDir);
 
@@ -59,7 +59,7 @@
 
     ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
 
-    ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+    ParsedPackage setSigningDetails(SigningDetails signingDetails);
 
     ParsedPackage setSplitCodePaths(String[] splitCodePaths);
 
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index cda4806..b1b46af 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -304,10 +304,6 @@
                 & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
     }
 
-    public boolean isDocumenter() {
-        return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
-    }
-
     public boolean isConfigurator() {
         return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
     }
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 2a5a721..fbe14ce 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -87,12 +87,13 @@
 import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageManager.PermissionWhitelistFlags;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.permission.CompatibilityPermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
@@ -2842,7 +2843,7 @@
                             // 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)) {
+                            if (!isCompatPlatformPermissionForPackage(perm, pkg)) {
                                 shouldGrantNormalPermission = false;
                             }
                         }
@@ -3380,14 +3381,12 @@
         return result;
     }
 
-    private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
+    private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
         boolean allowed = false;
-        final int NP = PackageParser.NEW_PERMISSIONS.length;
-        for (int ip=0; ip<NP; ip++) {
-            final PackageParser.NewPermissionInfo npi
-                    = PackageParser.NEW_PERMISSIONS[ip];
-            if (npi.name.equals(perm)
-                    && pkg.getTargetSdkVersion() < npi.sdkVersion) {
+        for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+            final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+            if (info.getName().equals(perm)
+                    && pkg.getTargetSdkVersion() < info.sdkVersion) {
                 allowed = true;
                 Log.i(TAG, "Auto-granting " + perm + " to old pkg "
                         + pkg.getPackageName());
@@ -3507,15 +3506,15 @@
         //     - or it shares a common signing certificate in its lineage with the defining package,
         //       and the defining package still trusts the old certificate for permissions
         //     - or it shares the above relationships with the system package
-        final PackageParser.SigningDetails sourceSigningDetails =
+        final SigningDetails sourceSigningDetails =
                 getSourcePackageSigningDetails(bp);
         return sourceSigningDetails.hasCommonSignerWithCapability(
                         pkg.getSigningDetails(),
-                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)
+                        SigningDetails.CertCapabilities.PERMISSION)
                 || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
                 || systemPackage.getSigningDetails().checkCapability(
                         pkg.getSigningDetails(),
-                        PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+                        SigningDetails.CertCapabilities.PERMISSION);
     }
 
     private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
@@ -3625,14 +3624,6 @@
             // Special permissions for the device configurator.
             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,
@@ -3673,11 +3664,11 @@
     }
 
     @NonNull
-    private PackageParser.SigningDetails getSourcePackageSigningDetails(
+    private SigningDetails getSourcePackageSigningDetails(
             @NonNull Permission bp) {
         final PackageSetting ps = getSourcePackageSetting(bp);
         if (ps == null) {
-            return PackageParser.SigningDetails.UNKNOWN;
+            return SigningDetails.UNKNOWN;
         }
         return ps.getSigningDetails();
     }
@@ -4233,33 +4224,35 @@
                 // If the target package is being uninstalled, we need to revoke this permission
                 // 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.getPackageName());
-                    if (bp.isRuntime()) {
-                        final int[] userIds = mUserManagerInt.getUserIds();
-                        final int numUserIds = userIds.length;
-                        for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
-                            final int userId = userIds[userIdNum];
-                            mPackageManagerInt.forEachPackage((AndroidPackage p) ->
-                                    revokePermissionFromPackageForUser(p.getPackageName(),
-                                            bp.getName(), true, userId, callback));
-                        }
-                    } else {
-                        mPackageManagerInt.forEachPackage(p -> {
+                    if (!isPermissionDeclaredByDisabledSystemPkg(bp)) {
+                        Slog.i(TAG, "Removing permission " + bp.getName()
+                                + " that used to be declared by " + bp.getPackageName());
+                        if (bp.isRuntime()) {
                             final int[] userIds = mUserManagerInt.getUserIds();
-                            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());
-                                }
+                            final int numUserIds = userIds.length;
+                            for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                                final int userId = userIds[userIdNum];
+                                mPackageManagerInt.forEachPackage((AndroidPackage p) ->
+                                        revokePermissionFromPackageForUser(p.getPackageName(),
+                                                bp.getName(), true, userId, callback));
                             }
-                        });
+                        } else {
+                            mPackageManagerInt.forEachPackage(p -> {
+                                final int[] userIds = mUserManagerInt.getUserIds();
+                                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());
+                                    }
+                                }
+                            });
+                        }
                     }
                     synchronized (mLock) {
                         mRegistry.removePermission(bp.getName());
@@ -4284,6 +4277,22 @@
         return changed;
     }
 
+    private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
+        final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+                    permission.getPackageName());
+        if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
+            final String permissionName = permission.getName();
+            final List<ParsedPermission> sourcePerms = disabledSourcePs.getPkg().getPermissions();
+            for (ParsedPermission sourcePerm : sourcePerms) {
+                if (TextUtils.equals(permissionName, sourcePerm.getName())
+                        && permission.getProtectionLevel() == sourcePerm.getProtectionLevel()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Revoke a runtime permission from a package for a given user ID.
      */
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index adf8f0d..844111a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -182,10 +182,10 @@
 
         if (!reusedMap.isEmpty()) {
             if (!wasHeaderPrinted) {
-                Signature[] signatures = pkg.getSigningDetails().signatures;
+                Signature[] signatures = pkg.getSigningDetails().getSignatures();
                 String signaturesDigest = signatures == null ? null : Arrays.toString(
                         PackageUtils.computeSignaturesSha256Digests(
-                                pkg.getSigningDetails().signatures, ":"));
+                                pkg.getSigningDetails().getSignatures(), ":"));
 
                 writer.println(pkgState.getPackageName() + ":");
                 writer.increaseIndent();
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 3c9b106..04b5005 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -16,10 +16,8 @@
 
 package com.android.server.policy;
 
-import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.ICameraService;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.hardware.display.DisplayManagerInternal;
@@ -27,13 +25,11 @@
 import android.os.HandlerExecutor;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.IDisplayFoldListener;
 
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
-import com.android.server.camera.CameraServiceProxy;
 import com.android.server.wm.WindowManagerInternal;
 
 /**
@@ -41,13 +37,8 @@
  * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
  */
 class DisplayFoldController {
-    private static final String TAG = "DisplayFoldController";
-
     private final WindowManagerInternal mWindowManagerInternal;
     private final DisplayManagerInternal mDisplayManagerInternal;
-    // Camera service proxy can be disabled through a config.
-    @Nullable
-    private final CameraServiceProxy mCameraServiceProxy;
     private final int mDisplayId;
     private final Handler mHandler;
 
@@ -64,12 +55,10 @@
 
     DisplayFoldController(
             Context context, WindowManagerInternal windowManagerInternal,
-            DisplayManagerInternal displayManagerInternal,
-            @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
+            DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
             Handler handler) {
         mWindowManagerInternal = windowManagerInternal;
         mDisplayManagerInternal = displayManagerInternal;
-        mCameraServiceProxy = cameraServiceProxy;
         mDisplayId = displayId;
         mFoldedArea = new Rect(foldedArea);
         mHandler = handler;
@@ -124,16 +113,6 @@
             }
         }
 
-        if (mCameraServiceProxy != null) {
-            if (folded) {
-                mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
-            } else {
-                mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
-            }
-        } else {
-            Slog.w(TAG, "Camera service unavailable to toggle folded state.");
-        }
-
         mDurationLogger.setDeviceFolded(folded);
         mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
         mFolded = folded;
@@ -188,8 +167,6 @@
                 LocalServices.getService(WindowManagerInternal.class);
         final DisplayManagerInternal displayService =
                 LocalServices.getService(DisplayManagerInternal.class);
-        final CameraServiceProxy cameraServiceProxy =
-                LocalServices.getService(CameraServiceProxy.class);
 
         final String configFoldedArea = context.getResources().getString(
                 com.android.internal.R.string.config_foldedArea);
@@ -201,6 +178,6 @@
         }
 
         return new DisplayFoldController(context, windowManagerService, displayService,
-                cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler());
+                displayId, foldedArea, DisplayThread.getHandler());
     }
 }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 89d5415..ad43514 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -71,6 +71,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
+import com.android.server.utils.TimingsTraceAndSlog;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -365,7 +366,11 @@
             return;
         }
 
+
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("Permission_grant_default_permissions-" + userId);
         grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
+        t.traceEnd();
 
         final OnInitializedCallback callback;
 
@@ -375,11 +380,15 @@
         }
 
         // Force synchronization as permissions might have changed
+        t.traceBegin("Permission_synchronize_permissions-" + userId);
         synchronizePermissionsAndAppOpsForUser(userId);
+        t.traceEnd();
 
         // Tell observers we are initialized for this user.
         if (callback != null) {
+            t.traceBegin("Permission_onInitialized-" + userId);
             callback.onInitialized(userId);
+            t.traceEnd();
         }
     }
 
@@ -394,6 +403,7 @@
 
     private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) {
         if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")");
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
         final PackageManagerInternal packageManagerInternal =
                 LocalServices.getService(PackageManagerInternal.class);
@@ -426,9 +436,12 @@
                         }
                     });
             try {
+                t.traceBegin("Permission_callback_waiting-" + userId);
                 future.get();
             } catch (InterruptedException | ExecutionException e) {
                 throw new IllegalStateException(e);
+            } finally {
+                t.traceEnd();
             }
 
             permissionControllerManager.updateUserSensitive();
@@ -494,14 +507,19 @@
      */
     private void synchronizePermissionsAndAppOpsForUser(@UserIdInt int userId) {
         if (DEBUG) Slog.i(LOG_TAG, "synchronizePermissionsAndAppOpsForUser(" + userId + ")");
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
         final PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
                 getUserContext(getContext(), UserHandle.of(userId)));
+        t.traceBegin("Permission_synchronize_addPackages-" + userId);
         packageManagerInternal.forEachPackage(
                 (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
+        t.traceEnd();
+        t.traceBegin("Permission_syncPackages-" + userId);
         synchronizer.syncPackages();
+        t.traceEnd();
     }
 
     private void resetAppOpPermissionsIfNotRequestedForUidAsync(int uid) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fb4d96e..e7636fc 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -261,6 +261,7 @@
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
     static final int SHORT_PRESS_POWER_GO_HOME = 4;
     static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
+    static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
 
     // must match: config_LongPressOnPowerBehavior in config.xml
     static final int LONG_PRESS_POWER_NOTHING = 0;
@@ -976,6 +977,14 @@
                     }
                     break;
                 }
+                case SHORT_PRESS_POWER_LOCK_OR_SLEEP: {
+                    if (keyguardOn()) {
+                        sleepDefaultDisplayFromPowerButton(eventTime, 0);
+                    } else {
+                        lockNow(null /*options*/);
+                    }
+                    break;
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
index c2596c7..3d47e92 100644
--- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
+++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
@@ -53,6 +53,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -308,6 +309,13 @@
                 dataOutputStream.writeInt(packageManagerInternal.getApplicationEnabledState(
                         pkg.getPackageName(), userId));
 
+                final List<String> requestedPermissions = pkg.getRequestedPermissions();
+                final int requestedPermissionsSize = requestedPermissions.size();
+                dataOutputStream.writeInt(requestedPermissionsSize);
+                for (int i = 0; i < requestedPermissionsSize; i++) {
+                    dataOutputStream.writeUTF(requestedPermissions.get(i));
+                }
+
                 final ArraySet<String> enabledComponents =
                         packageManagerInternal.getEnabledComponents(pkg.getPackageName(), userId);
                 final int enabledComponentsSize = CollectionUtils.size(enabledComponents);
@@ -323,7 +331,7 @@
                     dataOutputStream.writeUTF(disabledComponents.valueAt(i));
                 }
 
-                for (final Signature signature : pkg.getSigningDetails().signatures) {
+                for (final Signature signature : pkg.getSigningDetails().getSignatures()) {
                     dataOutputStream.write(signature.toByteArray());
                 }
             } catch (IOException e) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9638255..c39b5da 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -352,6 +352,12 @@
     // requested because it is updated asynchronously by the display power controller.
     private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
 
+    // The suspend blocker used to keep the CPU alive while the device is booting.
+    private final SuspendBlocker mBootingSuspendBlocker;
+
+    // True if the wake lock suspend blocker has been acquired.
+    private boolean mHoldingBootingSuspendBlocker;
+
     // The suspend blocker used to keep the CPU alive when an application has acquired
     // a wake lock.
     private final SuspendBlocker mWakeLockSuspendBlocker;
@@ -1036,10 +1042,16 @@
         }
 
         synchronized (mLock) {
+            mBootingSuspendBlocker =
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.Booting");
             mWakeLockSuspendBlocker =
                     mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
             mDisplaySuspendBlocker =
                     mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+            if (mBootingSuspendBlocker != null) {
+                mBootingSuspendBlocker.acquire();
+                mHoldingBootingSuspendBlocker = true;
+            }
             if (mDisplaySuspendBlocker != null) {
                 mDisplaySuspendBlocker.acquire();
                 mHoldingDisplaySuspendBlocker = true;
@@ -3385,6 +3397,10 @@
         }
 
         // First acquire suspend blockers if needed.
+        if (!mBootCompleted && !mHoldingBootingSuspendBlocker) {
+            mBootingSuspendBlocker.acquire();
+            mHoldingBootingSuspendBlocker = true;
+        }
         if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
             mWakeLockSuspendBlocker.acquire();
             mHoldingWakeLockSuspendBlocker = true;
@@ -3411,6 +3427,10 @@
         }
 
         // Then release suspend blockers if needed.
+        if (mBootCompleted && mHoldingBootingSuspendBlocker) {
+            mBootingSuspendBlocker.release();
+            mHoldingBootingSuspendBlocker = false;
+        }
         if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
             mWakeLockSuspendBlocker.release();
             mHoldingWakeLockSuspendBlocker = false;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 770779d..57d69c2 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -17,7 +17,6 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,13 +33,11 @@
 import android.os.PowerManagerInternal.LowPowerModeListener;
 import android.os.PowerSaveState;
 import android.os.UserHandle;
-import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.power.PowerManagerService;
@@ -70,7 +67,6 @@
     private final Object mLock;
     private final Context mContext;
     private final MyHandler mHandler;
-    private final FileUpdater mFileUpdater;
 
     private PowerManager mPowerManager;
 
@@ -212,7 +208,6 @@
         mHandler = new MyHandler(looper);
         mBatterySaverPolicy = policy;
         mBatterySaverPolicy.addListener(this);
-        mFileUpdater = new FileUpdater(context);
         mBatterySavingStats = batterySavingStats;
 
         PowerManager.invalidatePowerSaveModeCaches();
@@ -238,8 +233,6 @@
         filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
         mContext.registerReceiver(mReceiver, filter);
 
-        mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
-                .isRuntimeRestarted());
         mHandler.postSystemReady();
     }
 
@@ -436,7 +429,6 @@
 
         final boolean enabled;
         final boolean isInteractive = getPowerManager().isInteractive();
-        final ArrayMap<String, String> fileValues;
 
         synchronized (mLock) {
             enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked();
@@ -456,12 +448,6 @@
             listeners = mListeners.toArray(new LowPowerModeListener[0]);
 
             mIsInteractive = isInteractive;
-
-            if (enabled) {
-                fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
-            } else {
-                fileValues = null;
-            }
         }
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
@@ -471,12 +457,6 @@
 
         updateBatterySavingStats();
 
-        if (ArrayUtils.isEmpty(fileValues)) {
-            mFileUpdater.restoreDefault();
-        } else {
-            mFileUpdater.writeFiles(fileValues);
-        }
-
         if (sendBroadcast) {
 
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 09f8941..ffcb2bd 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -129,7 +129,11 @@
     @VisibleForTesting
     static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode";
 
+    /** @deprecated Old key used to set CPU frequency caps directly in sys files. */
+    @Deprecated
     private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
+    /** @deprecated Old key used to set CPU frequency caps directly in sys files. */
+    @Deprecated
     private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
 
     private static final String KEY_SUFFIX_ADAPTIVE = "_adaptive";
@@ -138,8 +142,6 @@
     static final Policy OFF_POLICY = new Policy(
             1f,    /* adjustBrightnessFactor */
             false, /* advertiseIsEnabled */
-            new CpuFrequencies(), /* cpuFrequenciesForInteractive */
-            new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
             false, /* deferFullBackup */
             false, /* deferKeyValueBackup */
             false, /* disableAnimation */
@@ -163,8 +165,6 @@
     private static final Policy DEFAULT_FULL_POLICY = new Policy(
             0.5f,  /* adjustBrightnessFactor */
             true,  /* advertiseIsEnabled */
-            new CpuFrequencies(), /* cpuFrequenciesForInteractive */
-            new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
             true,  /* deferFullBackup */
             true,  /* deferKeyValueBackup */
             false, /* disableAnimation */
@@ -487,8 +487,6 @@
         mEffectivePolicyRaw = new Policy(
                 rawPolicy.adjustBrightnessFactor,
                 rawPolicy.advertiseIsEnabled,
-                rawPolicy.cpuFrequenciesForInteractive,
-                rawPolicy.cpuFrequenciesForNoninteractive,
                 rawPolicy.deferFullBackup,
                 rawPolicy.deferKeyValueBackup,
                 rawPolicy.disableAnimation,
@@ -648,22 +646,6 @@
         public final boolean enableQuickDoze;
 
         /**
-         * List of CPU frequencies that should be written when battery saver is activated
-         * and the device is interactive.
-         *
-         * We use this to change the max CPU frequencies.
-         */
-        public final CpuFrequencies cpuFrequenciesForInteractive;
-
-        /**
-         * List of CPU frequencies that should be written when battery saver is activated
-         * and the device is non-interactive.
-         *
-         * We use this to change the max CPU frequencies.
-         */
-        public final CpuFrequencies cpuFrequenciesForNoninteractive;
-
-        /**
          * Whether to put all apps in the stand-by mode.
          */
         public final boolean forceAllAppsStandby;
@@ -687,8 +669,6 @@
         Policy(
                 float adjustBrightnessFactor,
                 boolean advertiseIsEnabled,
-                CpuFrequencies cpuFrequenciesForInteractive,
-                CpuFrequencies cpuFrequenciesForNoninteractive,
                 boolean deferFullBackup,
                 boolean deferKeyValueBackup,
                 boolean disableAnimation,
@@ -708,8 +688,6 @@
 
             this.adjustBrightnessFactor = Math.min(1, Math.max(0, adjustBrightnessFactor));
             this.advertiseIsEnabled = advertiseIsEnabled;
-            this.cpuFrequenciesForInteractive = cpuFrequenciesForInteractive;
-            this.cpuFrequenciesForNoninteractive = cpuFrequenciesForNoninteractive;
             this.deferFullBackup = deferFullBackup;
             this.deferKeyValueBackup = deferKeyValueBackup;
             this.disableAnimation = disableAnimation;
@@ -744,8 +722,6 @@
             mHashCode = Objects.hash(
                     adjustBrightnessFactor,
                     advertiseIsEnabled,
-                    cpuFrequenciesForInteractive,
-                    cpuFrequenciesForNoninteractive,
                     deferFullBackup,
                     deferKeyValueBackup,
                     disableAnimation,
@@ -772,16 +748,10 @@
 
             // Device-specific parameters.
             Map<String, String> deviceSpecificSettings = config.getDeviceSpecificSettings();
-            final String cpuFreqInteractive =
-                    deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_INTERACTIVE, "");
-            final String cpuFreqNoninteractive =
-                    deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_NONINTERACTIVE, "");
 
             return new Policy(
                     config.getAdjustBrightnessFactor(),
                     config.getAdvertiseIsEnabled(),
-                    (new CpuFrequencies()).parseString(cpuFreqInteractive),
-                    (new CpuFrequencies()).parseString(cpuFreqNoninteractive),
                     config.getDeferFullBackup(),
                     config.getDeferKeyValueBackup(),
                     config.getDisableAnimation(),
@@ -803,10 +773,6 @@
 
         BatterySaverPolicyConfig toConfig() {
             return new BatterySaverPolicyConfig.Builder()
-                    .addDeviceSpecificSetting(KEY_CPU_FREQ_INTERACTIVE,
-                            cpuFrequenciesForInteractive.toString())
-                    .addDeviceSpecificSetting(KEY_CPU_FREQ_NONINTERACTIVE,
-                            cpuFrequenciesForNoninteractive.toString())
                     .setAdjustBrightnessFactor(adjustBrightnessFactor)
                     .setAdvertiseIsEnabled(advertiseIsEnabled)
                     .setDeferFullBackup(deferFullBackup)
@@ -848,9 +814,6 @@
                         + deviceSpecificSettings);
             }
 
-            final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
-            final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");
-
             // Non-device-specific parameters.
             try {
                 parser.setString(settings == null ? "" : settings);
@@ -918,8 +881,6 @@
             return new Policy(
                     adjustBrightnessFactor,
                     advertiseIsEnabled,
-                    (new CpuFrequencies()).parseString(cpuFreqInteractive),
-                    (new CpuFrequencies()).parseString(cpuFreqNoninteractive),
                     deferFullBackup,
                     deferKeyValueBackup,
                     disableAnimation,
@@ -962,10 +923,7 @@
                     && forceAllAppsStandby == other.forceAllAppsStandby
                     && forceBackgroundCheck == other.forceBackgroundCheck
                     && locationMode == other.locationMode
-                    && soundTriggerMode == other.soundTriggerMode
-                    && cpuFrequenciesForInteractive.equals(other.cpuFrequenciesForInteractive)
-                    && cpuFrequenciesForNoninteractive.equals(
-                            other.cpuFrequenciesForNoninteractive);
+                    && soundTriggerMode == other.soundTriggerMode;
         }
 
         @Override
@@ -1179,14 +1137,6 @@
         }
     }
 
-    public ArrayMap<String, String> getFileValues(boolean interactive) {
-        synchronized (mLock) {
-            return interactive
-                    ? getCurrentPolicyLocked().cpuFrequenciesForInteractive.toSysFileMap()
-                    : getCurrentPolicyLocked().cpuFrequenciesForNoninteractive.toSysFileMap();
-        }
-    }
-
     public boolean isLaunchBoostDisabled() {
         synchronized (mLock) {
             return getCurrentPolicyLocked().disableLaunchBoost;
@@ -1274,17 +1224,6 @@
         pw.println(KEY_ENABLE_QUICK_DOZE + "=" + p.enableQuickDoze);
         pw.println(KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode);
 
-        pw.println("Interactive File values:");
-        pw.increaseIndent();
-        dumpMap(pw, p.cpuFrequenciesForInteractive.toSysFileMap());
-        pw.decreaseIndent();
-        pw.println();
-
-        pw.println("Noninteractive File values:");
-        pw.increaseIndent();
-        dumpMap(pw, p.cpuFrequenciesForNoninteractive.toSysFileMap());
-        pw.decreaseIndent();
-
         // Decrease from indent right after "Policy" line
         pw.decreaseIndent();
     }
diff --git a/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java b/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java
deleted file mode 100644
index 5b38cf4..0000000
--- a/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java
+++ /dev/null
@@ -1,142 +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.power.batterysaver;
-
-import android.util.ArrayMap;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Map;
-
-
-/**
- * Helper to parse a list of "core-number:frequency" pairs concatenated with / as a separator,
- * and convert them into a map of "filename -> value" that should be written to
- * /sys/.../scaling_max_freq.
- *
- * Example input: "0:1900800/4:2500000", which will be converted into:
- *   "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" "1900800"
- *   "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq" "2500000"
- *
- * Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
- */
-public class CpuFrequencies {
-    private static final String TAG = "CpuFrequencies";
-
-    private static final String CPU_DELIM = "/";
-    private static final String FREQUENCY_DELIM = ":";
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private final ArrayMap<Integer, Long> mCoreAndFrequencies = new ArrayMap<>();
-
-    public CpuFrequencies() {
-    }
-
-    /**
-     * Parse a string.
-     */
-    public CpuFrequencies parseString(String cpuNumberAndFrequencies) {
-        synchronized (mLock) {
-            mCoreAndFrequencies.clear();
-            try {
-                for (String pair : cpuNumberAndFrequencies.split(CPU_DELIM)) {
-                    pair = pair.trim();
-                    if (pair.length() == 0) {
-                        continue;
-                    }
-                    final String[] coreAndFreq = pair.split(FREQUENCY_DELIM, 2);
-
-                    if (coreAndFreq.length != 2) {
-                        throw new IllegalArgumentException("Wrong format");
-                    }
-                    final int core = Integer.parseInt(coreAndFreq[0]);
-                    final long freq = Long.parseLong(coreAndFreq[1]);
-
-                    mCoreAndFrequencies.put(core, freq);
-                }
-            } catch (IllegalArgumentException e) {
-                Slog.wtf(TAG, "Invalid configuration: '" + cpuNumberAndFrequencies + "'");
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Return a new map containing the filename-value pairs.
-     */
-    public ArrayMap<String, String> toSysFileMap() {
-        final ArrayMap<String, String> map = new ArrayMap<>();
-        addToSysFileMap(map);
-        return map;
-    }
-
-    /**
-     * Add the filename-value pairs to an existing map.
-     */
-    public void addToSysFileMap(Map<String, String> map) {
-        synchronized (mLock) {
-            final int size = mCoreAndFrequencies.size();
-
-            for (int i = 0; i < size; i++) {
-                final int core = mCoreAndFrequencies.keyAt(i);
-                final long freq = mCoreAndFrequencies.valueAt(i);
-
-                final String file = "/sys/devices/system/cpu/cpu" + Integer.toString(core) +
-                        "/cpufreq/scaling_max_freq";
-
-                map.put(file, Long.toString(freq));
-            }
-        }
-    }
-
-    /**
-     * Returns String describing the frequency settings used.
-     * The returned String can be parsed again by {@link #parseString(String)}.
-     */
-    public String toString() {
-        synchronized (mLock) {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < mCoreAndFrequencies.size(); i++) {
-                if (i > 0) {
-                    sb.append(CPU_DELIM);
-                }
-                sb.append(mCoreAndFrequencies.keyAt(i));
-                sb.append(FREQUENCY_DELIM);
-                sb.append(mCoreAndFrequencies.valueAt(i));
-            }
-
-            return sb.toString();
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        synchronized (mLock) {
-            if (this == obj) return true;
-            if (!(obj instanceof CpuFrequencies)) return false;
-            CpuFrequencies other = (CpuFrequencies) obj;
-            return mCoreAndFrequencies.equals(other.mCoreAndFrequencies);
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        return mCoreAndFrequencies.hashCode();
-    }
-}
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
deleted file mode 100644
index 1387617..0000000
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ /dev/null
@@ -1,406 +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.power.batterysaver;
-
-import android.content.Context;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-import com.android.server.IoThread;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Map;
-
-/**
- * Used by {@link BatterySaverController} to write values to /sys/ (and possibly /proc/ too) files
- * with retries. It also support restoring to the file original values.
- *
- * Retries are needed because writing to "/sys/.../scaling_max_freq" returns EIO when the current
- * frequency happens to be above the new max frequency.
- *
- * Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
- */
-public class FileUpdater {
-    private static final String TAG = BatterySaverController.TAG;
-
-    private static final boolean DEBUG = BatterySaverController.DEBUG;
-
-    /**
-     * If this system property is set to 1, it'll skip all file writes. This can be used when
-     * one needs to change max CPU frequency for benchmarking, for example.
-     */
-    private static final String PROP_SKIP_WRITE = "debug.batterysaver.no_write_files";
-
-    private static final String TAG_DEFAULT_ROOT = "defaults";
-
-    // Don't do disk access with this lock held.
-    private final Object mLock = new Object();
-
-    private final Context mContext;
-
-    private final Handler mHandler;
-
-    /**
-     * Filename -> value map that holds pending writes.
-     */
-    @GuardedBy("mLock")
-    private final ArrayMap<String, String> mPendingWrites = new ArrayMap<>();
-
-    /**
-     * Filename -> value that holds the original value of each file.
-     */
-    @GuardedBy("mLock")
-    private final ArrayMap<String, String> mDefaultValues = new ArrayMap<>();
-
-    /** Number of retries. We give up on writing after {@link #MAX_RETRIES} retries. */
-    @GuardedBy("mLock")
-    private int mRetries = 0;
-
-    private final int MAX_RETRIES;
-
-    private final long RETRY_INTERVAL_MS;
-
-    /**
-     * "Official" constructor. Don't use the other constructor in the production code.
-     */
-    public FileUpdater(Context context) {
-        this(context, IoThread.get().getLooper(), 10, 5000);
-    }
-
-    /**
-     * Constructor for test.
-     */
-    @VisibleForTesting
-    FileUpdater(Context context, Looper looper, int maxRetries, int retryIntervalMs) {
-        mContext = context;
-        mHandler = new Handler(looper);
-
-        MAX_RETRIES = maxRetries;
-        RETRY_INTERVAL_MS = retryIntervalMs;
-    }
-
-    public void systemReady(boolean runtimeRestarted) {
-        synchronized (mLock) {
-            if (runtimeRestarted) {
-                // If it runtime restarted, read the original values from the disk and apply.
-                if (loadDefaultValuesLocked()) {
-                    Slog.d(TAG, "Default values loaded after runtime restart; writing them...");
-                    restoreDefault();
-                }
-            } else {
-                // Delete it, without checking the result. (file-not-exist is not an exception.)
-                injectDefaultValuesFilename().delete();
-            }
-        }
-    }
-
-    /**
-     * Write values to files. (Note the actual writes happen ASAP but asynchronously.)
-     */
-    public void writeFiles(ArrayMap<String, String> fileValues) {
-        synchronized (mLock) {
-            for (int i = fileValues.size() - 1; i >= 0; i--) {
-                final String file = fileValues.keyAt(i);
-                final String value = fileValues.valueAt(i);
-
-                if (DEBUG) {
-                    Slog.d(TAG, "Scheduling write: '" + value + "' to '" + file + "'");
-                }
-
-                mPendingWrites.put(file, value);
-
-            }
-            mRetries = 0;
-
-            mHandler.removeCallbacks(mHandleWriteOnHandlerRunnable);
-            mHandler.post(mHandleWriteOnHandlerRunnable);
-        }
-    }
-
-    /**
-     * Restore the default values.
-     */
-    public void restoreDefault() {
-        synchronized (mLock) {
-            if (DEBUG) {
-                Slog.d(TAG, "Resetting file default values.");
-            }
-            mPendingWrites.clear();
-
-            writeFiles(mDefaultValues);
-        }
-    }
-
-    private Runnable mHandleWriteOnHandlerRunnable = () -> handleWriteOnHandler();
-
-    /** Convert map keys into a single string for debug messages. */
-    private String getKeysString(Map<String, String> source) {
-        return new ArrayList<>(source.keySet()).toString();
-    }
-
-    /** Clone an ArrayMap. */
-    private ArrayMap<String, String> cloneMap(ArrayMap<String, String> source) {
-        return new ArrayMap<>(source);
-    }
-
-    /**
-     * Called on the handler and writes {@link #mPendingWrites} to the disk.
-     *
-     * When it about to write to each file for the first time, it'll read the file and store
-     * the original value in {@link #mDefaultValues}.
-     */
-    private void handleWriteOnHandler() {
-        // We don't want to access the disk with the lock held, so copy the pending writes to
-        // a local map.
-        final ArrayMap<String, String> writes;
-        synchronized (mLock) {
-            if (mPendingWrites.size() == 0) {
-                return;
-            }
-
-            if (DEBUG) {
-                Slog.d(TAG, "Writing files: (# retries=" + mRetries + ") " +
-                        getKeysString(mPendingWrites));
-            }
-
-            writes = cloneMap(mPendingWrites);
-        }
-
-        // Then write.
-
-        boolean needRetry = false;
-
-        final int size = writes.size();
-        for (int i = 0; i < size; i++) {
-            final String file = writes.keyAt(i);
-            final String value = writes.valueAt(i);
-
-            // Make sure the default value is loaded.
-            if (!ensureDefaultLoaded(file)) {
-                continue;
-            }
-
-            // Write to the file. When succeeded, remove it from the pending list.
-            // Otherwise, schedule a retry.
-            try {
-                injectWriteToFile(file, value);
-
-                removePendingWrite(file);
-            } catch (IOException e) {
-                needRetry = true;
-            }
-        }
-        if (needRetry) {
-            scheduleRetry();
-        }
-    }
-
-    private void removePendingWrite(String file) {
-        synchronized (mLock) {
-            mPendingWrites.remove(file);
-        }
-    }
-
-    private void scheduleRetry() {
-        synchronized (mLock) {
-            if (mPendingWrites.size() == 0) {
-                return; // Shouldn't happen but just in case.
-            }
-
-            mRetries++;
-            if (mRetries > MAX_RETRIES) {
-                doWtf("Gave up writing files: " + getKeysString(mPendingWrites));
-                return;
-            }
-
-            mHandler.removeCallbacks(mHandleWriteOnHandlerRunnable);
-            mHandler.postDelayed(mHandleWriteOnHandlerRunnable, RETRY_INTERVAL_MS);
-        }
-    }
-
-    /**
-     * Make sure {@link #mDefaultValues} has the default value loaded for {@code file}.
-     *
-     * @return true if the default value is loaded. false if the file cannot be read.
-     */
-    private boolean ensureDefaultLoaded(String file) {
-        // Has the default already?
-        synchronized (mLock) {
-            if (mDefaultValues.containsKey(file)) {
-                return true;
-            }
-        }
-        final String originalValue;
-        try {
-            originalValue = injectReadFromFileTrimmed(file);
-        } catch (IOException e) {
-            // If the file is not readable, assume can't write too.
-            injectWtf("Unable to read from file", e);
-
-            removePendingWrite(file);
-            return false;
-        }
-        synchronized (mLock) {
-            mDefaultValues.put(file, originalValue);
-            saveDefaultValuesLocked();
-        }
-        return true;
-    }
-
-    @VisibleForTesting
-    String injectReadFromFileTrimmed(String file) throws IOException {
-        return IoUtils.readFileAsString(file).trim();
-    }
-
-    @VisibleForTesting
-    void injectWriteToFile(String file, String value) throws IOException {
-        if (injectShouldSkipWrite()) {
-            Slog.i(TAG, "Skipped writing to '" + file + "'");
-            return;
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "Writing: '" + value + "' to '" + file + "'");
-        }
-        try (FileWriter out = new FileWriter(file)) {
-            out.write(value);
-        } catch (IOException | RuntimeException e) {
-            Slog.w(TAG, "Failed writing '" + value + "' to '" + file + "': " + e.getMessage());
-            throw e;
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void saveDefaultValuesLocked() {
-        final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
-
-        FileOutputStream outs = null;
-        try {
-            file.getBaseFile().getParentFile().mkdirs();
-            outs = file.startWrite();
-
-            // Write to XML
-            TypedXmlSerializer out = Xml.resolveSerializer(outs);
-            out.startDocument(null, true);
-            out.startTag(null, TAG_DEFAULT_ROOT);
-
-            XmlUtils.writeMapXml(mDefaultValues, out, null);
-
-            // Epilogue.
-            out.endTag(null, TAG_DEFAULT_ROOT);
-            out.endDocument();
-
-            // Close.
-            file.finishWrite(outs);
-        } catch (IOException | XmlPullParserException | RuntimeException e) {
-            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
-            file.failWrite(outs);
-        }
-    }
-
-    @GuardedBy("mLock")
-    @VisibleForTesting
-    boolean loadDefaultValuesLocked() {
-        final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
-        if (DEBUG) {
-            Slog.d(TAG, "Loading from " + file.getBaseFile());
-        }
-        Map<String, String> read = null;
-        try (FileInputStream in = file.openRead()) {
-            TypedXmlPullParser parser = Xml.resolvePullParser(in);
-
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type != XmlPullParser.START_TAG) {
-                    continue;
-                }
-                final int depth = parser.getDepth();
-                // Check the root tag
-                final String tag = parser.getName();
-                if (depth == 1) {
-                    if (!TAG_DEFAULT_ROOT.equals(tag)) {
-                        Slog.e(TAG, "Invalid root tag: " + tag);
-                        return false;
-                    }
-                    continue;
-                }
-                final String[] tagName = new String[1];
-                read = (ArrayMap<String, String>) XmlUtils.readThisArrayMapXml(parser,
-                        TAG_DEFAULT_ROOT, tagName, null);
-            }
-        } catch (FileNotFoundException e) {
-            read = null;
-        } catch (IOException | XmlPullParserException | RuntimeException e) {
-            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
-        }
-        if (read != null) {
-            mDefaultValues.clear();
-            mDefaultValues.putAll(read);
-            return true;
-        }
-        return false;
-    }
-
-    private void doWtf(String message) {
-        injectWtf(message, null);
-    }
-
-    @VisibleForTesting
-    void injectWtf(String message, Throwable e) {
-        Slog.wtf(TAG, message, e);
-    }
-
-    File injectDefaultValuesFilename() {
-        final File dir = new File(Environment.getDataSystemDirectory(), "battery-saver");
-        dir.mkdirs();
-        return new File(dir, "default-values.xml");
-    }
-
-    @VisibleForTesting
-    boolean injectShouldSkipWrite() {
-        return SystemProperties.getBoolean(PROP_SKIP_WRITE, false);
-    }
-
-    @VisibleForTesting
-    ArrayMap<String, String> getDefaultValuesForTest() {
-        return mDefaultValues;
-    }
-}
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index 3d78828..141d4dc 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -44,6 +44,8 @@
                     return requestLskf();
                 case "clear-lskf":
                     return clearLskf();
+                case "is-lskf-captured":
+                    return isLskfCaptured();
                 case "reboot-and-apply":
                     return rebootAndApply();
                 default:
@@ -74,6 +76,14 @@
         return 0;
     }
 
+    private int isLskfCaptured() throws RemoteException {
+        String packageName = getNextArgRequired();
+        boolean captured = mService.isLskfCaptured(packageName);
+        PrintWriter pw = getOutPrintWriter();
+        pw.printf("%s LSKF capture status: %s\n", packageName, captured ? "true" : "false");
+        return 0;
+    }
+
     private int rebootAndApply() throws RemoteException {
         String packageName = getNextArgRequired();
         String rebootReason = getNextArgRequired();
@@ -90,8 +100,9 @@
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Recovery system commands:");
-        pw.println("  request-lskf <token>");
+        pw.println("  request-lskf <package_name>");
         pw.println("  clear-lskf");
-        pw.println("  reboot-and-apply <token> <reason>");
+        pw.println("  is-lskf-captured <package_name>");
+        pw.println("  reboot-and-apply <package_name> <reason>");
     }
 }
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9560f59..7bf3478 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -116,10 +116,10 @@
     static final int ROLLBACK_STATE_DELETED = 4;
 
     /**
-     * The session ID for the staged session if this rollback data represents a staged session,
-     * {@code -1} otherwise.
+     * The session ID associate with this rollback. This is the parent session ID in the case of
+     * a multi-package session.
      */
-    private final int mStagedSessionId;
+    private final int mOriginalSessionId;
 
     /**
      * The rollback info for this rollback.
@@ -181,13 +181,6 @@
     private final int[] mPackageSessionIds;
 
     /**
-     * The number of sessions in the install which are notified with success by
-     * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
-     * This rollback will be enabled only after all child sessions finished with success.
-     */
-    private int mNumPackageSessionsWithSuccess;
-
-    /**
      * The extension versions supported at the time of rollback creation.
      */
     @NonNull private final SparseIntArray mExtensionVersions;
@@ -203,24 +196,25 @@
      *
      * @param rollbackId the id of the rollback.
      * @param backupDir the directory where the rollback data is stored.
-     * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
+     * @param originalSessionId the session id associated with this rollback.
+     * @param isStaged true if this is a staged rollback.
      * @param userId the user that performed the install with rollback enabled.
      * @param installerPackageName the installer package name from the original install session.
      * @param packageSessionIds the session ids for all packages in the install.
      * @param extensionVersions the extension versions supported at the time of rollback creation
      */
-    Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+    Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId,
             String installerPackageName, int[] packageSessionIds,
             SparseIntArray extensionVersions) {
         this.info = new RollbackInfo(rollbackId,
                 /* packages */ new ArrayList<>(),
-                /* isStaged */ stagedSessionId != -1,
+                /* isStaged */ isStaged,
                 /* causePackages */ new ArrayList<>(),
                 /* committedSessionId */ -1);
         mUserId = userId;
         mInstallerPackageName = installerPackageName;
         mBackupDir = backupDir;
-        mStagedSessionId = stagedSessionId;
+        mOriginalSessionId = originalSessionId;
         mState = ROLLBACK_STATE_ENABLING;
         mTimestamp = Instant.now();
         mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
@@ -228,16 +222,10 @@
         mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
     }
 
-    Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
-             String installerPackageName) {
-        this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
-                new SparseIntArray(0));
-    }
-
     /**
      * Constructs a pre-populated Rollback instance.
      */
-    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId,
             @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress,
             int userId, String installerPackageName, SparseIntArray extensionVersions) {
         this.info = info;
@@ -245,7 +233,7 @@
         mInstallerPackageName = installerPackageName;
         mBackupDir = backupDir;
         mTimestamp = timestamp;
-        mStagedSessionId = stagedSessionId;
+        mOriginalSessionId = originalSessionId;
         mState = state;
         mStateDescription = stateDescription;
         mRestoreUserDataInProgress = restoreUserDataInProgress;
@@ -298,12 +286,11 @@
     }
 
     /**
-     * Returns the session ID for the staged session if this rollback data represents a staged
-     * session, or {@code -1} otherwise.
+     * Returns the session ID associated with this rollback, or {@code -1} if unknown.
      */
     @AnyThread
-    int getStagedSessionId() {
-        return mStagedSessionId;
+    int getOriginalSessionId() {
+        return mOriginalSessionId;
     }
 
     /**
@@ -838,17 +825,6 @@
     }
 
     /**
-     * Called when a child session finished with success.
-     * Returns true when all child sessions are notified with success. This rollback will be
-     * enabled only after all child sessions finished with success.
-     */
-    @WorkerThread
-    boolean notifySessionWithSuccess() {
-        assertInWorkerThread();
-        return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
-    }
-
-    /**
      * Returns true if all packages in this rollback are enabled. We won't enable this rollback
      * until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
      * automatically when the embedding apex is enabled.
@@ -954,9 +930,8 @@
         ipw.println("-state: " + getStateAsString());
         ipw.println("-stateDescription: " + mStateDescription);
         ipw.println("-timestamp: " + getTimestamp());
-        if (getStagedSessionId() != -1) {
-            ipw.println("-stagedSessionId: " + getStagedSessionId());
-        }
+        ipw.println("-isStaged: " + isStaged());
+        ipw.println("-originalSessionId: " + getOriginalSessionId());
         ipw.println("-packages:");
         ipw.increaseIndent();
         for (PackageRollbackInfo pkg : info.getPackages()) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e19f57..f7ed000 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -247,11 +247,6 @@
                                 PackageManagerInternal.class);
                         pm.setEnableRollbackCode(token, ret);
                     });
-
-                    // We're handling the ordered broadcast. Abort the
-                    // broadcast because there is no need for it to go to
-                    // anyone else.
-                    abortBroadcast();
                 }
             }
         }, enableRollbackFilter, null, getHandler());
@@ -611,11 +606,11 @@
                 }
 
                 PackageInstaller.SessionInfo session = mContext.getPackageManager()
-                        .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
+                        .getPackageInstaller().getSessionInfo(rollback.getOriginalSessionId());
                 if (session == null || session.isStagedSessionFailed()) {
                     if (rollback.isEnabling()) {
                         iter.remove();
-                        deleteRollback(rollback, "Session " + rollback.getStagedSessionId()
+                        deleteRollback(rollback, "Session " + rollback.getOriginalSessionId()
                                 + " not existed or failed");
                     }
                     continue;
@@ -789,7 +784,13 @@
             newRollback = createNewRollback(parentSession);
         }
 
-        return enableRollbackForPackageSession(newRollback, packageSession);
+        if (enableRollbackForPackageSession(newRollback, packageSession)) {
+            // Persist the rollback if all packages are enabled. We will make the rollback
+            // available once the whole session is installed successfully.
+            return newRollback.allPackagesEnabled() ? completeEnableRollback(newRollback) : true;
+        } else {
+            return false;
+        }
     }
 
     @WorkerThread
@@ -978,43 +979,10 @@
             throw new SecurityException("notifyStagedSession may only be called by the system.");
         }
 
-        // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
-        // to preserve the invariant that all operations that modify state happen there.
         return awaitResult(() -> {
             assertInWorkerThread();
-            PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
-
-            final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
-            if (session == null) {
-                Slog.e(TAG, "No matching install session for: " + sessionId);
-                return -1;
-            }
-
-            Rollback newRollback = createNewRollback(session);
-            if (!session.isMultiPackage()) {
-                if (!enableRollbackForPackageSession(newRollback, session)) {
-                    Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
-                }
-            } else {
-                for (int childSessionId : session.getChildSessionIds()) {
-                    final PackageInstaller.SessionInfo childSession =
-                            installer.getSessionInfo(childSessionId);
-                    if (childSession == null) {
-                        Slog.e(TAG, "No matching child install session for: " + childSessionId);
-                        break;
-                    }
-                    if (!enableRollbackForPackageSession(newRollback, childSession)) {
-                        Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
-                        break;
-                    }
-                }
-            }
-
-            if (!completeEnableRollback(newRollback)) {
-                return -1;
-            } else {
-                return newRollback.info.getRollbackId();
-            }
+            Rollback rollback = getRollbackForSession(sessionId);
+            return rollback != null ? rollback.info.getRollbackId() : -1;
         });
     }
 
@@ -1124,21 +1092,25 @@
                 Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
             }
 
+            Rollback rollback = getRollbackForSession(sessionId);
+            if (rollback == null || !rollback.isEnabling()
+                    || sessionId != rollback.getOriginalSessionId()) {
+                // We only care about the parent session id which will tell us whether the
+                // whole session is successful or not.
+                return;
+            }
             if (success) {
-                Rollback rollback = getRollbackForSession(sessionId);
-                if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
-                        && rollback.notifySessionWithSuccess()
-                        && completeEnableRollback(rollback)) {
+                if (!rollback.isStaged() && completeEnableRollback(rollback)) {
+                    // completeEnableRollback() ensures the rollback is deleted if not all packages
+                    // are enabled. For staged rollbacks, we will make them available in
+                    // onBootCompleted().
                     makeRollbackAvailable(rollback);
                 }
             } else {
-                Rollback rollback = getRollbackForSession(sessionId);
-                if (rollback != null && rollback.isEnabling()) {
-                    Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
-                            + " for failed session id=" + sessionId);
-                    mRollbacks.remove(rollback);
-                    deleteRollback(rollback, "Session " + sessionId + " failed");
-                }
+                Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+                        + " for failed session id=" + sessionId);
+                mRollbacks.remove(rollback);
+                deleteRollback(rollback, "Session " + sessionId + " failed");
             }
         }
     }
@@ -1307,7 +1279,7 @@
             rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
                     installerPackageName, packageSessionIds, getExtensionVersions());
         } else {
-            rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+            rollback = mRollbackStore.createNonStagedRollback(rollbackId, parentSessionId, userId,
                     installerPackageName, packageSessionIds, getExtensionVersions());
         }
 
@@ -1339,7 +1311,7 @@
         // We expect mRollbacks to be a very small list; linear search should be plenty fast.
         for (int i = 0; i < mRollbacks.size(); ++i) {
             Rollback rollback = mRollbacks.get(i);
-            if (rollback.getStagedSessionId() == sessionId
+            if (rollback.getOriginalSessionId() == sessionId
                     || rollback.containsSessionId(sessionId)) {
                 return rollback;
             }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index fc62f5b..6b783f7 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -209,23 +209,24 @@
      * Creates a new Rollback instance for a non-staged rollback with
      * backupDir assigned.
      */
-    Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
-            int[] packageSessionIds, SparseIntArray extensionVersions) {
+    Rollback createNonStagedRollback(int rollbackId, int originalSessionId, int userId,
+            String installerPackageName, int[] packageSessionIds,
+            SparseIntArray extensionVersions) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
-                packageSessionIds, extensionVersions);
+        return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ false, userId,
+                installerPackageName, packageSessionIds, extensionVersions);
     }
 
     /**
      * Creates a new Rollback instance for a staged rollback with
      * backupDir assigned.
      */
-    Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+    Rollback createStagedRollback(int rollbackId, int originalSessionId, int userId,
             String installerPackageName, int[] packageSessionIds,
             SparseIntArray extensionVersions) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
-                packageSessionIds, extensionVersions);
+        return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, userId,
+                installerPackageName, packageSessionIds, extensionVersions);
     }
 
     private static boolean isLinkPossible(File oldFile, File newFile) {
@@ -312,7 +313,7 @@
             JSONObject dataJson = new JSONObject();
             dataJson.put("info", rollbackInfoToJson(rollback.info));
             dataJson.put("timestamp", rollback.getTimestamp().toString());
-            dataJson.put("stagedSessionId", rollback.getStagedSessionId());
+            dataJson.put("originalSessionId", rollback.getOriginalSessionId());
             dataJson.put("state", rollback.getStateAsString());
             dataJson.put("stateDescription", rollback.getStateDescription());
             dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
@@ -380,7 +381,9 @@
                 rollbackInfoFromJson(dataJson.getJSONObject("info")),
                 backupDir,
                 Instant.parse(dataJson.getString("timestamp")),
-                dataJson.getInt("stagedSessionId"),
+                // Backward compatibility: Historical rollbacks are not erased upon OTA update.
+                //  Need to load the old field 'stagedSessionId' as fallback.
+                dataJson.optInt("originalSessionId", dataJson.optInt("stagedSessionId", -1)),
                 rollbackStateFromString(dataJson.getString("state")),
                 dataJson.optString("stateDescription"),
                 dataJson.getBoolean("restoreUserDataInProgress"),
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index a582914..82ba60f 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -53,17 +53,15 @@
     private static final String TAG = RemoteRotationResolverService.class.getSimpleName();
 
     private final long mIdleUnbindTimeoutMs;
-    private final Object mLock;
 
     RemoteRotationResolverService(Context context, ComponentName serviceName,
-            int userId, long idleUnbindTimeoutMs, Object lock) {
+            int userId, long idleUnbindTimeoutMs) {
         super(context,
                 new Intent(RotationResolverService.SERVICE_INTERFACE).setComponent(serviceName),
                 BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
                 IRotationResolverService.Stub::asInterface);
 
         mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
-        mLock = lock;
 
         // Bind right away.
         connect();
@@ -75,8 +73,7 @@
         return -1;
     }
 
-    @GuardedBy("mLock")
-    public void resolveRotationLocked(RotationRequest request) {
+    public void resolveRotation(RotationRequest request) {
         final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
         post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
 
@@ -97,13 +94,15 @@
         @NonNull
         private final IRotationResolverCallback mIRotationResolverCallback;
         @NonNull
-        private ICancellationSignal mCancellation;
-        @NonNull
         private final CancellationSignal mCancellationSignalInternal;
         @NonNull
         final RotationResolverInternal.RotationResolverCallbackInternal
                 mCallbackInternal;
 
+        @NonNull
+        @GuardedBy("mLock")
+        private ICancellationSignal mCancellation;
+
         @GuardedBy("mLock")
         boolean mIsFulfilled;
 
@@ -111,17 +110,19 @@
         final RotationResolutionRequest mRemoteRequest;
 
         boolean mIsDispatched;
-        private final Object mLock = new Object();
+        private final Object mLock;
         private final long mRequestStartTimeMillis;
 
         RotationRequest(
                 @NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
-                RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
+                RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal,
+                Object lock) {
             mCallbackInternal = callbackInternal;
             mRemoteRequest = request;
             mIRotationResolverCallback = new RotationResolverCallback(this);
             mCancellationSignalInternal = cancellationSignal;
             mRequestStartTimeMillis = SystemClock.elapsedRealtime();
+            mLock = lock;
         }
 
 
@@ -153,7 +154,7 @@
         }
 
         private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
-            private WeakReference<RotationRequest> mRequestWeakReference;
+            private final WeakReference<RotationRequest> mRequestWeakReference;
 
             RotationResolverCallback(RotationRequest request) {
                 this.mRequestWeakReference = new WeakReference<>(request);
@@ -215,7 +216,6 @@
                         }
                     }
                 }
-
             }
         }
     }
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 165a1d6..48d8fed 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -121,26 +121,26 @@
         final RotationResolverInternal.RotationResolverCallbackInternal wrapper =
                 new RotationResolverInternal.RotationResolverCallbackInternal() {
 
-            @Override
-            public void onSuccess(int result) {
-                synchronized (mLock) {
-                    mLatencyTracker
-                            .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
-                }
-                callbackInternal.onSuccess(result);
-            }
+                    @Override
+                    public void onSuccess(int result) {
+                        synchronized (mLock) {
+                            mLatencyTracker
+                                    .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+                        }
+                        callbackInternal.onSuccess(result);
+                    }
 
-            @Override
-            public void onFailure(int error) {
-                synchronized (mLock) {
-                    mLatencyTracker
-                            .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
-                }
-                callbackInternal.onFailure(error);
-            }
-        };
+                    @Override
+                    public void onFailure(int error) {
+                        synchronized (mLock) {
+                            mLatencyTracker
+                                    .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+                        }
+                        callbackInternal.onFailure(error);
+                    }
+                };
         mCurrentRequest = new RemoteRotationResolverService.RotationRequest(wrapper,
-                request, cancellationSignalInternal);
+                request, cancellationSignalInternal, mLock);
 
         cancellationSignalInternal.setOnCancelListener(() -> {
             synchronized (mLock) {
@@ -152,7 +152,7 @@
         });
 
 
-        mRemoteService.resolveRotationLocked(mCurrentRequest);
+        mRemoteService.resolveRotation(mCurrentRequest);
         mCurrentRequest.mIsDispatched = true;
     }
 
@@ -160,7 +160,7 @@
     private void ensureRemoteServiceInitiated() {
         if (mRemoteService == null) {
             mRemoteService = new RemoteRotationResolverService(getContext(), mComponentName,
-                    getUserId(), CONNECTION_TTL_MILLIS, mLock);
+                    getUserId(), CONNECTION_TTL_MILLIS);
         }
     }
 
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 6366280..e006b65 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,24 +21,23 @@
 import android.hardware.audio.common.V2_0.Uuid;
 import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
 import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.Properties;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
 import android.media.audio.common.AudioConfig;
 import android.media.audio.common.AudioOffloadInfo;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
 import android.os.HidlMemory;
 import android.os.HidlMemoryUtil;
 import android.os.ParcelFileDescriptor;
@@ -55,9 +54,9 @@
  */
 class ConversionUtil {
     static @NonNull
-    SoundTriggerModuleProperties hidl2aidlProperties(
+    Properties hidl2aidlProperties(
             @NonNull ISoundTriggerHw.Properties hidlProperties) {
-        SoundTriggerModuleProperties aidlProperties = new SoundTriggerModuleProperties();
+        Properties aidlProperties = new Properties();
         aidlProperties.implementor = hidlProperties.implementor;
         aidlProperties.description = hidlProperties.description;
         aidlProperties.version = hidlProperties.version;
@@ -75,9 +74,9 @@
         return aidlProperties;
     }
 
-    static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
-            @NonNull Properties hidlProperties) {
-        SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+    static @NonNull Properties hidl2aidlProperties(
+            @NonNull android.hardware.soundtrigger.V2_3.Properties hidlProperties) {
+        Properties aidlProperties = hidl2aidlProperties(hidlProperties.base);
         aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
         aidlProperties.audioCapabilities =
                 hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities);
@@ -216,9 +215,11 @@
     }
 
     static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig(
-            @NonNull RecognitionConfig aidlConfig) {
+            @NonNull RecognitionConfig aidlConfig, int deviceHandle, int ioHandle) {
         android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
                 new android.hardware.soundtrigger.V2_3.RecognitionConfig();
+        hidlConfig.base.header.captureDevice = deviceHandle;
+        hidlConfig.base.header.captureHandle = ioHandle;
         hidlConfig.base.header.captureRequested = aidlConfig.captureRequested;
         for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
             hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
@@ -299,8 +300,6 @@
         aidlEvent.status = hidl2aidlRecognitionStatus(hidlEvent.status);
         aidlEvent.type = hidl2aidlSoundModelType(hidlEvent.type);
         aidlEvent.captureAvailable = hidlEvent.captureAvailable;
-        // hidlEvent.captureSession is never a valid field.
-        aidlEvent.captureSession = -1;
         aidlEvent.captureDelayMs = hidlEvent.captureDelayMs;
         aidlEvent.capturePreambleMs = hidlEvent.capturePreambleMs;
         aidlEvent.triggerInData = hidlEvent.triggerInData;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
new file mode 100644
index 0000000..2f2cb59
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.os.HwBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This is the basic implementation of HalFactory, which uses either the default STHAL or a mock.
+ *
+ * The choice of which HAL to use is as follows:
+ * - Get the (int) value of "debug.soundtrigger_middleware.use_mock_hal" sysprop, if it doesn't
+ *   exist, assume 0.
+ * - If the value is 0, use the default HAL on the device. Connect to the latest-version "default"
+ *   instance declared in the device manifest (either AIDL or HIDL).
+ * - If the value is 2, connect to a "mock" instance of the latest v2.x (HIDL).
+ * - If the value is 3, connect to a "mock" instance of soundtrigger3 (AIDL).
+ * - Otherwise, throw.
+ */
+class DefaultHalFactory implements HalFactory {
+    private static final String TAG = "SoundTriggerMiddlewareDefaultHalFactory";
+
+    private static final @NonNull ICaptureStateNotifier mCaptureStateNotifier =
+            new ExternalCaptureStateTracker();
+
+    private static final int USE_DEFAULT_HAL = 0;
+    private static final int USE_MOCK_HAL_V2 = 2;
+    private static final int USE_MOCK_HAL_V3 = 3;
+
+    @Override
+    public ISoundTriggerHal create() {
+        try {
+            int mockHal = SystemProperties.getInt("debug.soundtrigger_middleware.use_mock_hal",
+                    USE_DEFAULT_HAL);
+            if (mockHal == USE_DEFAULT_HAL) {
+                // Use production HAL.
+
+                // Try soundtrigger3 (AIDL) first.
+                final String aidlServiceName =
+                        android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+                                + "/default";
+                if (ServiceManager.isDeclared(aidlServiceName)) {
+                    Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+                    return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+                            () -> {
+                                // This property needs to be defined in an init.rc script and
+                                // trigger a HAL reboot.
+                                SystemProperties.set("sys.audio.restart.hal", "1");
+                            });
+                }
+
+                // Fallback to soundtrigger-V2.x (HIDL).
+                Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+                ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
+                return SoundTriggerHw2Compat.create(driver, () -> {
+                    // This property needs to be defined in an init.rc script and
+                    // trigger a HAL reboot.
+                    SystemProperties.set("sys.audio.restart.hal", "1");
+                }, mCaptureStateNotifier);
+            } else if (mockHal == USE_MOCK_HAL_V2) {
+                // Use V2 mock.
+                Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+                HwBinder.setTrebleTestingOverride(true);
+                try {
+                    ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
+                    return SoundTriggerHw2Compat.create(driver, () -> {
+                        try {
+                            driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
+                        } catch (Exception e) {
+                            Log.e(TAG, "Failed to reboot mock HAL", e);
+                        }
+                    }, mCaptureStateNotifier);
+                } finally {
+                    HwBinder.setTrebleTestingOverride(false);
+                }
+            } else if (mockHal == USE_MOCK_HAL_V3) {
+                // Use V3 mock.
+                final String aidlServiceName =
+                        android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+                                + "/mock";
+                Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+                return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+                        () -> {
+                            try {
+                                ServiceManager.waitForService(aidlServiceName).shellCommand(null,
+                                        null, null, new String[]{"reboot"}, null, null);
+                            } catch (Exception e) {
+                                Log.e(TAG, "Failed to reboot mock HAL", e);
+                            }
+                        });
+            } else {
+                throw new RuntimeException("Unknown HAL mock version: " + mockHal);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+}
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 9404904..d195fbe 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,42 +16,55 @@
 
 package com.android.server.soundtrigger_middleware;
 
+import android.annotation.NonNull;
 import android.util.Log;
 
+import java.util.LinkedList;
+import java.util.List;
 import java.util.concurrent.Semaphore;
-import java.util.function.Consumer;
 
 /**
  * This is a never-give-up listener for sound trigger external capture state notifications, as
  * published by the audio policy service.
  *
  * This class will constantly try to connect to the service over a background thread and tolerate
- * its death. The client will be notified by a single provided function that is called in a
- * synchronized manner.
- * For simplicity, there is currently no way to stop the tracker. This is possible to add if the
- * need ever arises.
+ * its death.
  */
-class ExternalCaptureStateTracker {
+class ExternalCaptureStateTracker implements ICaptureStateNotifier {
     private static final String TAG = "CaptureStateTracker";
-    /** Our client's listener. */
-    private final Consumer<Boolean> mListener;
+
+    /** Our client's listeners. Also used as lock. */
+    private final List<Listener> mListeners = new LinkedList<>();
+
+    /** Conservatively, until notified otherwise, we assume capture is active. */
+    private boolean mCaptureActive = true;
+
     /** This semaphore will get a permit every time we need to reconnect. */
     private final Semaphore mNeedToConnect = new Semaphore(1);
 
     /**
      * Constructor. Will start a background thread to do the work.
-     *
-     * @param listener A client provided listener that will be called on state
-     *                 changes. May be
-     *                 called multiple consecutive times with the same value. Never
-     *                 called
-     *                 concurrently.
      */
-    ExternalCaptureStateTracker(Consumer<Boolean> listener) {
-        mListener = listener;
+    ExternalCaptureStateTracker() {
         new Thread(this::run).start();
     }
 
+
+    @Override
+    public boolean registerListener(@NonNull Listener listener) {
+        synchronized (mListeners) {
+            mListeners.add(listener);
+            return mCaptureActive;
+        }
+    }
+
+    @Override
+    public void unregisterListener(Listener listener) {
+        synchronized (mListeners) {
+            mListeners.remove(listener);
+        }
+    }
+
     /**
      * Routine for the background thread. Keeps trying to reconnect.
      */
@@ -74,7 +87,12 @@
      */
     private void setCaptureState(boolean active) {
         try {
-            mListener.accept(active);
+            synchronized (mListeners) {
+                mCaptureActive = active;
+                for (Listener listener : mListeners) {
+                    listener.onCaptureStateChange(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/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
index b19e2ed..6da8a79 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -16,16 +16,14 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
-
 /**
- * A factory for creating instances of {@link ISoundTriggerHw}.
+ * A factory for creating instances of {@link ISoundTriggerHal}.
  *
  * @hide
  */
 public interface HalFactory {
     /**
-     * @return An instance of {@link ISoundTriggerHw}.
+     * @return An instance of {@link ISoundTriggerHal}.
      */
-    ISoundTriggerHw create();
+    ISoundTriggerHal create();
 }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
new file mode 100644
index 0000000..07d83ca
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * Allow registering listeners for tracking changes in audio capture state (when recording starts /
+ * stops). The client will be notified in a synchronized manner.
+ */
+interface ICaptureStateNotifier {
+    interface Listener {
+        void onCaptureStateChange(boolean state);
+    }
+
+    /**
+     * Register a listener for state change notifications. Returns the current capture state and
+     * any subsequent changes will be sent to the listener.
+     * @param listener The listener.
+     * @return The state at the time of registration.
+     */
+    boolean registerListener(@NonNull Listener listener);
+
+    /**
+     * Unregister a listener, previously registered with {@link #registerListener(Listener)}.
+     * Once this call returns, no more invocations of the listener will be made.
+     */
+    void unregisterListener(@NonNull Listener listener);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
new file mode 100644
index 0000000..aa85dd0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
+
+/**
+ * This interface mimics the soundtrigger HAL interface, with a few key differences:
+ * <ul>
+ * <li>Generally, methods should not throw, except for the following cases:
+ *   <ul>
+ *   <li>Any unexpected HAL behavior is considered an internal HAL malfunction and should be thrown
+ *   as a {@link HalException}, from any method.
+ *   <li>A {@link RuntimeException} with a {@link android.os.DeadObjectException} cause represents
+ *   a dead HAL process and may be thrown by any method.
+ *   <li>Implementations of earlier versions of the interface may throw a
+ *   {@link RecoverableException} with a
+ *   {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED} for methods that
+ *   have been introduced in later versions of the interface.
+ *   <li>Certain methods are allowed to throw a {@link RecoverableException} with a
+ *   {@link android.media.soundtrigger.Status#RESOURCE_CONTENTION} to indicate transient
+ *   failures.
+ *   </ul>
+ * <li>Some binder-specific details are hidden.
+ * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
+ * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
+ * them.
+ * </ul>
+ * For cases where the client wants to explicitly handle specific versions of the underlying driver
+ * interface, they may call {@link #interfaceDescriptor()}.
+ * <p>
+ * <b>Note to maintainers</b>: This class must always be kept in sync with the latest version,
+ * so that clients have access to the entire functionality without having to burden themselves with
+ * compatibility, as much as possible.
+ */
+interface ISoundTriggerHal {
+    /**
+     * @see ISoundTriggerHw#getProperties()
+     */
+    Properties getProperties();
+
+    /**
+     * @see ISoundTriggerHw#registerGlobalCallback(ISoundTriggerHwGlobalCallback)
+     */
+    void registerCallback(GlobalCallback callback);
+
+    /**
+     * @see ISoundTriggerHw#loadSoundModel(android.media.soundtrigger.SoundModel,
+     * ISoundTriggerHwCallback)
+     */
+    int loadSoundModel(SoundModel soundModel, ModelCallback callback);
+
+    /**
+     * @see ISoundTriggerHw#loadPhraseSoundModel(android.media.soundtrigger.PhraseSoundModel,
+     * ISoundTriggerHwCallback)
+     */
+    int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback);
+
+    /**
+     * @see ISoundTriggerHw#unloadSoundModel(int)
+     */
+    void unloadSoundModel(int modelHandle);
+
+    /**
+     * @see ISoundTriggerHw#startRecognition(int, int, int, RecognitionConfig)
+     */
+    void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config);
+
+    /**
+     * @see ISoundTriggerHw#stopRecognition(int)
+     */
+    void stopRecognition(int modelHandle);
+
+    /**
+     * @see ISoundTriggerHw#forceRecognitionEvent(int)
+     */
+    void forceRecognitionEvent(int modelHandle);
+
+    /**
+     * @return null if not supported.
+     * @see ISoundTriggerHw#queryParameter(int, int)
+     */
+    ModelParameterRange queryParameter(int modelHandle, int param);
+
+    /**
+     * @see ISoundTriggerHw#getParameter(int, int)
+     */
+    int getModelParameter(int modelHandle, int param);
+
+    /**
+     * @see ISoundTriggerHw#setParameter(int, int, int)
+     */
+    void setModelParameter(int modelHandle, int param, int value);
+
+    /**
+     * @see IBinder#getInterfaceDescriptor()
+     */
+    String interfaceDescriptor();
+
+    /**
+     * @see IBinder#linkToDeath(IBinder.DeathRecipient, int)
+     */
+    void linkToDeath(IBinder.DeathRecipient recipient);
+
+    /**
+     * @see IBinder#unlinkToDeath(IBinder.DeathRecipient, int)
+     */
+    void unlinkToDeath(IBinder.DeathRecipient recipient);
+
+    /*
+     * This is only useful for testing decorators and doesn't actually do anything with the real
+     * HAL. This method would block until all callbacks that were previously generated have been
+     * invoked. For most decorators, this merely flushes the delegate, but for delegates that may
+     * have additional buffers for callbacks this should flush them.
+     */
+    void flushCallbacks();
+
+    /**
+     * Kill and restart the HAL instance. This is typically a last resort for error recovery and may
+     * result in other related services being killed.
+     */
+    void reboot();
+
+    /**
+     * Called when this interface is guaranteed to no longer be used and can free up any resources
+     * used.
+     */
+    void detach();
+
+    /**
+     * Callback interface for model-related events.
+     */
+    interface ModelCallback {
+        /**
+         * @see ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent)
+         */
+        void recognitionCallback(int modelHandle, RecognitionEvent event);
+
+        /**
+         * @see ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent)
+         */
+        void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event);
+
+        /**
+         * @see ISoundTriggerHwCallback#modelUnloaded(int)
+         */
+        void modelUnloaded(int modelHandle);
+    }
+
+    /**
+     * Callback interface for global events.
+     */
+    interface GlobalCallback {
+        /**
+         * @see ISoundTriggerHwGlobalCallback#onResourcesAvailable()
+         */
+        void onResourcesAvailable();
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
deleted file mode 100644
index 8b434bd..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hidl.base.V1_0.IBase;
-import android.os.IHwBinder;
-
-/**
- * This interface mimics android.hardware.soundtrigger.V2_x.ISoundTriggerHw and
- * android.hardware.soundtrigger.V2_x.ISoundTriggerHwCallback, with a few key differences:
- * <ul>
- * <li>Methods in the original interface generally have a status return value and potentially a
- * second return value which is the actual return value. This is reflected via a synchronous
- * callback, which is not very pleasant to work with. This interface replaces that pattern with
- * the convention that a HalException is thrown for non-OK status, and then we can use the
- * return value for the actual return value.
- * <li>This interface will always include all the methods from the latest 2.x version (and thus
- * from every 2.x version) interface, with the convention that unsupported methods throw a
- * {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
- * code.
- * <li>Cases where the original interface had multiple versions of a method representing the exact
- * thing, or there exists a trivial conversion between the new and old version, this interface
- * represents only the latest version, without any _version suffixes.
- * <li>Removes some of the obscure IBinder methods.
- * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
- * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
- * them.
- * <li>soundModelCallback has been removed, since nobody cares about it. Implementations are free
- * to silently discard it.
- * </ul>
- * For cases where the client wants to explicitly handle specific versions of the underlying driver
- * interface, they may call {@link #interfaceDescriptor()}.
- * <p>
- * <b>Note to maintainers</b>: This class must always be kept in sync with the latest 2.x version,
- * so that clients have access to the entire functionality without having to burden themselves with
- * compatibility, as much as possible.
- */
-public interface ISoundTriggerHw2 {
-    /**
-     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
-     * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
-     */
-    android.hardware.soundtrigger.V2_3.Properties getProperties();
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback)
-     */
-    int loadSoundModel(
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
-            SoundTriggerHw2Compat.Callback callback, int cookie);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadPhraseSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback)
-     */
-    int loadPhraseSoundModel(
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
-            SoundTriggerHw2Compat.Callback callback, int cookie);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#unloadSoundModel(int)
-     */
-    void unloadSoundModel(int modelHandle);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopRecognition(int)
-     */
-    void stopRecognition(int modelHandle);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopAllRecognitions()
-     */
-    void stopAllRecognitions();
-
-    /**
-     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int,
-     * android.hardware.soundtrigger.V2_3.RecognitionConfig,
-     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
-     */
-    void startRecognition(int modelHandle,
-            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
-            SoundTriggerHw2Compat.Callback callback, int cookie);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getModelState(int)
-     */
-    void getModelState(int modelHandle);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getParameter(int, int,
-     * ISoundTriggerHw.getParameterCallback)
-     */
-    int getModelParameter(int modelHandle, int param);
-
-    /**
-     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#setParameter(int, int, int)
-     */
-    void setModelParameter(int modelHandle, int param, int value);
-
-    /**
-     * @return null if not supported.
-     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#queryParameter(int, int,
-     * ISoundTriggerHw.queryParameterCallback)
-     */
-    ModelParameterRange queryParameter(int modelHandle, int param);
-
-    /**
-     * @see IHwBinder#linkToDeath(IHwBinder.DeathRecipient, long)
-     */
-    boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie);
-
-    /**
-     * @see IHwBinder#unlinkToDeath(IHwBinder.DeathRecipient)
-     */
-    boolean unlinkToDeath(IHwBinder.DeathRecipient recipient);
-
-    /**
-     * @see IBase#interfaceDescriptor()
-     */
-    String interfaceDescriptor() throws android.os.RemoteException;
-
-    interface Callback {
-        /**
-         * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#recognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent,
-         * int)
-         */
-        void recognitionCallback(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
-                int cookie);
-
-        /**
-         * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#phraseRecognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent,
-         * int)
-         */
-        void phraseRecognitionCallback(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
-                int cookie);
-    }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
index a90053a..60f89da 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
@@ -16,18 +16,18 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import android.media.ICaptureStateListener;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 
 /**
- * This interface unifies methods from ISoundTriggerMiddlewareService and ICaptureStateListener.
+ * This interface closely follows ISoundTriggerMiddlewareService with some subtle changes for
+ * convenience.
  *
  * The ISoundTriggerMiddlewareService have been modified to exclude identity information and the
  * RemoteException signature, both of which are only relevant at the service boundary layer.
  */
-public interface ISoundTriggerMiddlewareInternal extends ICaptureStateListener {
+public interface ISoundTriggerMiddlewareInternal {
     /**
      * Query the available modules and their capabilities.
      */
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
index 416548d..016e5c9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/README.md
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -1,19 +1,145 @@
 # Sound Trigger Middleware
-TODO: Add component description.
 
-## Notes about thread synchronization
+## Overview
+Sound Trigger Middleware is a system service that exposes sound trigger functionality (low-power
+detection of acoustic events) to applications and higher-level system service.
+
+It has the following roles:
+- Isolating the soundtrigger HAL from potentially untrusted clients.
+- Enforcing correct behavior of the clients.
+- Enforcing correct behavior of the HAL and attempting to recover from failures.
+- Enforcing permissions for using soundtrigger functionality.
+- Serializing access to the HAL.
+- Logging soundtrigger usage in a comprehensive and consistent manner.
+- Generating a dumpsys report including current state and history of operations.
+- Providing a standard interface regardless which version of the HAL is implemented and gracefully
+  degrading operation whenever necessary.
+
+## Structure
+
+The service implementation can be divided into three main layers:
+
+- The "bottom layer" is concerned with HAL compatibility - making all HAL versions look and behave
+  the same.
+- The "middle layer" is concerned with the business logic of the service.
+- The "top layer" is concerned with exposing this functionality as a System Service and integrating
+  with other parts of the system.
+
+### HAL Compatibility Layer
+
+This layer implements the `ISoundTriggerHal` interface, which is the version-agnostic representation
+of the sound trigger HAL driver. It has two main implementations, `SoundTriggerHw2Compat` and
+`SoundTriggerHw3Compat` responsible for adapting to V2.x and V3 HAL drivers, respectively, including
+supporting their respective minor-version differences.
+
+This layer also includes several `ISoundTriggerHal` decorators, such as `SoundTriggerHalWatchdog`
+that enforces deadlines on calls into the HAL, and `SoundTriggerHalEnforcer` which enforces that
+the HAL respects the expected protocol.
+
+The decorator-based design is an effective tool for separation of aspects and modularity, thus
+keeping classes relatively small and focused on one concern. It is also very effective for
+testability by following dependency injection principles.
+
+### Business Logic Layer
+
+This layer also uses a decorator-based design for separation of concerns. The main interface being
+decorated is `ISoundTriggerMiddlwareInternal`, which closely follows the external-facing AIDL
+interface, `ISoundTriggerMiddlewareService`.
+
+Each of the decorators serves a focused purpose: for example, `SoundTriggerMiddlwarePermission`
+deals with enforcing permissions required for the various methods, `SoundTriggerMiddlewareLogging`
+logs all API usage, `SoundTriggerMiddlewareValidation` enforces correct usage of the protocol and
+isolates client errors from internal server errors.
+
+At the bottom of this decorator stack is `SoundTriggerMiddlewareImpl` / `SoundTriggerModule`, which
+are the adapter between `ISoundTriggerHal` and `ISoundTriggerMiddlwareInternal`, introducing the
+notion of having separate client sessions sharing the same HAL.
+
+### Service Layer
+
+This layer ties everything together. It instantiates the actual system service and the decorator
+stack. It also provides concrete connections to the Audio service (for negotiating sessions shared
+between Audio and Sound Trigger and for notifications about audio recording) and to the various HAL
+factories.
+
+This is the only layer that makes strong assumptions about the environment instead of relying on
+abstractions.
+
+## Error Handling and Exception Conventions
+
+We follow conventions for usage of exceptions in the service, in order to correctly and consistently
+distinguish the following cases:
+
+1. The client has done something wrong.
+2. The service implementation has done something wrong.
+3. The HAL has done something wrong.
+4. Nobody has done anything wrong, but runtime conditions prevent an operation from being fulfilled
+  as intended.
+
+The `SoundTriggerMiddlewarePermission` class would reject any calls from unauthorized clients,
+responding with the appropriate exception.
+
+The `SoundTriggerMiddlewareValidation` class does much of this separation. By validating the
+client's data and state, it would throw a relevant `RuntimeException` exception to the client
+without passing the requests down to the lower layers. Once that is done, any exception thrown from
+the underlying implementation can be assumed to be not the client's fault. If caught, they will be
+classified according to the following rule:
+
+- If they are `RecoverableException`s, they represent category #4 above, and will be presented to
+  the client as `ServiceSpecificException`s with the same error code.
+- Otherwise, they are considered an internal error (including HAL malfunction) and will be
+  presented to the client as `ServiceSpecificException(Status.INTERNAL_ERROR)`.
+
+Internally, we would throw `RecoverableException` whenever appropriate. Whenever a HAL malfunctions,
+`SoundTriggerHalEnforcer` is responsible for rebooting it and throwing an exception. A HAL death is
+considered a valid failure mode, and thus result in `RecoverableException(Status.DEAD_OBJECT)`,
+which ends up as a `ServiceSpecificException(Status.DEAD_OBJECT)` on the client side.
+
+## Notes About Thread Synchronization
 This component has some tricky thread synchronization considerations due to its layered design and
 due to the fact that it is involved in both in-bound and out-bound calls from / to
-external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
-nesting locks. The order is:
-- `SoundTriggerMiddlewareValidation` lock.
-- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
-  inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
-  within our calls into `AudioSessionProvider.acquireSession()`.
-- `SoundTriggerModule` lock.
+external components.
 
-This dictates careful consideration of callbacks going from `SoundTriggerModule` to
-`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
-We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
-`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
-in `SoundTriggerMiddlewareValidation` and avoid the lock.
+The following mutexes need to be considered:
+- Typically, a one or more mutexes that exist in every layer of the sound trigger middleware stack
+  to serialize access to its internal state or to external components.
+- Audio Policy Service lock. This one is external - it should be assumed to be held whenever we're
+  inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
+  within our calls into `AudioSessionProvider.acquireSession()` /
+  `AudioSessionProvider.releaseSession()`.
+
+To avoid potential deadlocks, a strict locking order must be ensured whenever nesting locks. The
+order is:
+- Upper layers of the stack, starting from the top (i.e. may not attempt to acquire a higher-layer
+  mutex while a lower-layer mutex is being held) until `ISoundTriggerHw2`.
+- Audio Policy Service lock.
+- Lower layers of the stack, starting from `ISoundTriggerHw2` all the way down to the HAL.
+
+In order to enforce this order, some conventions are established around when it is safe for a module
+to call another module, while having its local mutex(es) held:
+- Most calls (see exceptions below) originating from SoundTriggerMiddlewareService simply propagate
+  down the decorator stack. It is legal to call into the next layer down while holding a local
+  mutex. It is illegal to invoke a callback with a local mutex held.
+- Callbacks propagate from the lower layers up to the upper layers. It is legal to hold a local
+  mutex within a callback, but **not** while call to an upper layer.
+- In order to be able to synchronize, despite the asynchronous nature of callbacks,
+  `stopRecognition()` and `unloadModel()` work differently. They guarantee that once they return,
+  the callbacks associated with them will no longer be called. This implies that they have to block
+  until any pending callbacks are done processing and since these callbacks are potentially holding
+  locks of higher-order mutexes, we must not be holding a local mutex while calling down. The proper
+  sequence for these calls is:
+  - Obtain the local lock if needed. Update/check local state as necessary.
+  - Call the respective method of the delegate ("downwards"). Once it returns, not more callbacks
+    related to this operation will be called.
+  - Obtain the local lock if needed. Update local state as necessary. Assume that state might have
+    changed while the lock has been released.
+  - Release the local lock.
+  - Invoke any synchronous callbacks if needed.
+- Calling from `SoundTriggerMiddlewareImpl` / `SoundTriggerModule` into the audio policy service via
+  `acquireSession()` / `releaseSession()` while holding the local lock is legal.
+- `setCaptureState()` calls, originating from Audio Policy Service, into the lower layers of the
+  stack may call into the HAL (specifically, they must invoke `stopRecognition()`, but must not
+  block on callbacks. For this reason, `SoundTriggerHw2ConcurrentCaptureHandler`, which is the 
+  recipient of these calls, features a buffer and an additional thread, which allows the actual
+  stopping to be synchronous, as required, without having to block the call upon higher layers
+  processing the callbacks.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
new file mode 100644
index 0000000..e3ce719
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of concurrent capture
+ * constraints, for HAL implementations older than V2.4 (later versions support this feature at the
+ * HAL level).
+ * <p>
+ * Decorating an instance with this class would result in all active recognitions being aborted as
+ * soon as capture state becomes active. This class ensures consistent handling of abortions coming
+ * from that HAL and abortions coming from concurrent capture, in that only one abort event will be
+ * delivered, irrespective of the relative timing of the two events.
+ * <p>
+ * There are some delicate thread-safety issues handled here:
+ * <ul>
+ * <li>When a model is stopped via stopRecognition(), we guarantee that by the time the call
+ * returns, there will be no more recognition events (including abort) delivered for this model.
+ * This implies synchronous stopping and blocking until all pending events have been delivered.
+ * <li>When a model is stopped via onCaptureStateChange(true), the stopping of the recognition at
+ * the HAL level must be synchronous, but the call must not block on the delivery of the
+ * callbacks, due to the risk of a deadlock: the onCaptureStateChange() calls are typically
+ * invoked with the audio policy mutex held, so must not call method which may attempt to lock
+ * higher-level mutexes. See README.md in this directory for further details.
+ * </ul>
+ * The way this behavior is achieved is by having an additional thread with an event queue, which
+ * joins together model events coming from the delegate module with abort events originating from
+ * this layer (as result of external capture).
+ */
+public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal,
+        ICaptureStateNotifier.Listener {
+    private final @NonNull ISoundTriggerHal mDelegate;
+    private GlobalCallback mGlobalCallback;
+
+    /**
+     * Information about a model that is currently loaded. This is needed in order to be able to
+     * send abort events to its designated callback.
+     */
+    private static class LoadedModel {
+        final int type;
+        final @NonNull ModelCallback callback;
+
+        private LoadedModel(int type, @NonNull ModelCallback callback) {
+            this.type = type;
+            this.callback = callback;
+        }
+    }
+
+    /**
+     * This map holds the model type for every model that is loaded.
+     */
+    private final @NonNull Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>();
+
+    /**
+     * A set of all models that are currently active.
+     * We use this in order to know which models to stop in case of external capture.
+     * Used as a lock to synchronize operations that effect activity.
+     */
+    private final @NonNull Set<Integer> mActiveModels = new HashSet<>();
+
+    /**
+     * Notifier for changes in capture state.
+     */
+    private final @NonNull ICaptureStateNotifier mNotifier;
+
+    /**
+     * Whether capture is active.
+     */
+    private boolean mCaptureState;
+
+    /**
+     * Since we're wrapping the death recipient, we need to keep a translation map for unlinking.
+     * Key is the client recipient, value is the wrapper.
+     */
+    private final @NonNull Map<IBinder.DeathRecipient, IBinder.DeathRecipient>
+            mDeathRecipientMap = new ConcurrentHashMap<>();
+
+    private final @NonNull CallbackThread mCallbackThread = new CallbackThread();
+
+    public SoundTriggerHalConcurrentCaptureHandler(
+            @NonNull ISoundTriggerHal delegate,
+            @NonNull ICaptureStateNotifier notifier) {
+        mDelegate = delegate;
+        mNotifier = notifier;
+        mCaptureState = mNotifier.registerListener(this);
+    }
+
+    @Override
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        synchronized (mActiveModels) {
+            if (mCaptureState) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+            mActiveModels.add(modelHandle);
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        synchronized (mActiveModels) {
+            mDelegate.stopRecognition(modelHandle);
+            mActiveModels.remove(modelHandle);
+        }
+        // Block until all previous events are delivered. Since this is potentially blocking on
+        // upward calls, it must be done outside the lock.
+        mCallbackThread.flush();
+    }
+
+    @Override
+    public void onCaptureStateChange(boolean active) {
+        synchronized (mActiveModels) {
+            if (active) {
+                // Abort all active models. This must be done as one transaction to the event
+                // thread, in order to be able to dedupe events before they are delivered.
+                try (SafeCloseable ignored = mCallbackThread.stallReader()) {
+                    for (int modelHandle : mActiveModels) {
+                        mDelegate.stopRecognition(modelHandle);
+                        LoadedModel model = mLoadedModels.get(modelHandle);
+                        // An abort event must be the last one for its model.
+                        mCallbackThread.pushWithDedupe(modelHandle, true,
+                                () -> notifyAbort(modelHandle, model));
+                    }
+                }
+            } else {
+                mGlobalCallback.onResourcesAvailable();
+            }
+
+            mCaptureState = active;
+        }
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        int handle = mDelegate.loadSoundModel(soundModel, new CallbackWrapper(callback));
+        mLoadedModels.put(handle, new LoadedModel(SoundModelType.GENERIC, callback));
+        return handle;
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+            ModelCallback callback) {
+        int handle = mDelegate.loadPhraseSoundModel(soundModel, new CallbackWrapper(callback));
+        mLoadedModels.put(handle, new LoadedModel(SoundModelType.KEYPHRASE, callback));
+        return handle;
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        mLoadedModels.remove(modelHandle);
+        mDelegate.unloadSoundModel(modelHandle);
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        mGlobalCallback = new GlobalCallback() {
+            @Override
+            public void onResourcesAvailable() {
+                mCallbackThread.push(callback::onResourcesAvailable);
+            }
+        };
+        mDelegate.registerCallback(mGlobalCallback);
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        IBinder.DeathRecipient wrapper = new IBinder.DeathRecipient() {
+            @Override
+            public void binderDied() {
+                mCallbackThread.push(() -> recipient.binderDied());
+            }
+        };
+        mDelegate.linkToDeath(wrapper);
+        mDeathRecipientMap.put(recipient, wrapper);
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mDelegate.unlinkToDeath(mDeathRecipientMap.remove(recipient));
+    }
+
+    private class CallbackWrapper implements ISoundTriggerHal.ModelCallback {
+        private final @NonNull ISoundTriggerHal.ModelCallback mDelegateCallback;
+
+        private CallbackWrapper(@NonNull ModelCallback delegateCallback) {
+            mDelegateCallback = delegateCallback;
+        }
+
+        @Override
+        public void recognitionCallback(int modelHandle, RecognitionEvent event) {
+            // A recognition event must be the last one for its model, unless it is a forced one
+            // (those leave the model active).
+            mCallbackThread.pushWithDedupe(modelHandle,
+                    event.status != RecognitionStatus.FORCED,
+                    () -> mDelegateCallback.recognitionCallback(modelHandle, event));
+        }
+
+        @Override
+        public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) {
+            // A recognition event must be the last one for its model, unless it is a forced one
+            // (those leave the model active).
+            mCallbackThread.pushWithDedupe(modelHandle,
+                    event.common.status != RecognitionStatus.FORCED,
+                    () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event));
+        }
+
+        @Override
+        public void modelUnloaded(int modelHandle) {
+            mCallbackThread.push(() -> mDelegateCallback.modelUnloaded(modelHandle));
+        }
+    }
+
+    @Override
+    public void flushCallbacks() {
+        mDelegate.flushCallbacks();
+        mCallbackThread.flush();
+    }
+
+    /**
+     * This is a thread for asynchronous delivery of callback events, having the following features:
+     * <ul>
+     * <li>Events are processed on a separate thread than the thread that pushed them, in the order
+     * they were pushed.
+     * <li>Events can be deduped upon entry to the queue. This is achieved as follows:
+     * <ul>
+     *     <li>Temporarily stall the reader via {@link #stallReader()}.
+     *     <li>Within this scope, push as many events as needed via
+     *     {@link #pushWithDedupe(int, boolean, Runnable)}.
+     *     If an event with the same model handle as the one being pushed is already in the queue
+     *     and has been marked as "lastForModel", the new event will be discarded before entering
+     *     the queue.
+     *     <li>Finally, un-stall the reader by existing the scope.
+     *     <li>Events that do not require deduping can be pushed via {@link #push(Runnable)}.
+     * </ul>
+     * <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior
+     * to this call have been fully processed.
+     * </ul>
+     */
+    private static class CallbackThread {
+        private static class Entry {
+            final boolean lastForModel;
+            final int modelHandle;
+            final Runnable runnable;
+
+            private Entry(boolean lastForModel, int modelHandle, Runnable runnable) {
+                this.lastForModel = lastForModel;
+                this.modelHandle = modelHandle;
+                this.runnable = runnable;
+            }
+        }
+
+        private boolean mStallReader = false;
+        private final Queue<Entry> mList = new LinkedList<>();
+        private int mPushCount = 0;
+        private int mProcessedCount = 0;
+
+        /**
+         * Ctor. Starts the thread.
+         */
+        CallbackThread() {
+            new Thread(() -> {
+                try {
+                    while (true) {
+                        pop().run();
+                        synchronized (mList) {
+                            mProcessedCount++;
+                            mList.notifyAll();
+                        }
+                    }
+                } catch (InterruptedException e) {
+                    // If interrupted, exit.
+                }
+            }).start();
+        }
+
+        /**
+         * Push a new runnable to the queue, with no deduping.
+         *
+         * @param runnable The runnable to push.
+         */
+        void push(Runnable runnable) {
+            pushEntry(new Entry(false, 0, runnable), false);
+        }
+
+
+        /**
+         * Push a new runnable to the queue, with deduping.
+         * If an entry with the same model handle is already in the queue and was designated as
+         * last for model, this one will be discarded.
+         *
+         * @param modelHandle The model handle, used for deduping purposes.
+         * @param lastForModel If true, this entry will be considered the last one for this model
+         *                     and any subsequence calls for this handle (whether lastForModel or
+         *                     not) will be discarded while this entry is in the queue.
+         * @param runnable    The runnable to push.
+         */
+        void pushWithDedupe(int modelHandle, boolean lastForModel, Runnable runnable) {
+            pushEntry(new Entry(lastForModel, modelHandle, runnable), true);
+        }
+
+        /**
+         * Block until every entry pushed prior to this call has been processed.
+         */
+        void flush() {
+            try {
+                synchronized (mList) {
+                    int pushCount = mPushCount;
+                    while (mProcessedCount != pushCount) {
+                        mList.wait();
+                    }
+                }
+            } catch (InterruptedException ignored) {
+            }
+        }
+
+        /**
+         * Creates a scope (using a try-with-resources block), within which events that are pushed
+         * remain queued and processed. This is useful in order to utilize deduping.
+         */
+        SafeCloseable stallReader() {
+            synchronized (mList) {
+                mStallReader = true;
+                return () -> {
+                    synchronized (mList) {
+                        mStallReader = false;
+                        mList.notifyAll();
+                    }
+                };
+            }
+        }
+
+        private void pushEntry(Entry entry, boolean dedupe) {
+            synchronized (mList) {
+                if (dedupe) {
+                    for (Entry existing : mList) {
+                        if (existing.lastForModel && existing.modelHandle == entry.modelHandle) {
+                            return;
+                        }
+                    }
+                }
+                mList.add(entry);
+                mPushCount++;
+                mList.notifyAll();
+            }
+        }
+
+        private Runnable pop() throws InterruptedException {
+            synchronized (mList) {
+                while (mStallReader || mList.isEmpty()) {
+                    mList.wait();
+                }
+                return mList.remove().runnable;
+            }
+        }
+    }
+
+    /** Notify the client that recognition has been aborted. */
+    private static void notifyAbort(int modelHandle, LoadedModel model) {
+        switch (model.type) {
+            case SoundModelType.GENERIC: {
+                RecognitionEvent event = new RecognitionEvent();
+                event.status = RecognitionStatus.ABORTED;
+                event.type = SoundModelType.GENERIC;
+                model.callback.recognitionCallback(modelHandle, event);
+            }
+            break;
+
+            case SoundModelType.KEYPHRASE: {
+                PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+                event.common.status = RecognitionStatus.ABORTED;
+                event.common.type = SoundModelType.KEYPHRASE;
+                model.callback.phraseRecognitionCallback(modelHandle, event);
+            }
+            break;
+        }
+    }
+
+    @Override
+    public void detach() {
+        mDelegate.detach();
+        mNotifier.unregisterListener(this);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // All methods below do trivial delegation - no interesting logic.
+    @Override
+    public void reboot() {
+        mDelegate.reboot();
+    }
+
+    @Override
+    public Properties getProperties() {
+        return mDelegate.getProperties();
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        mDelegate.forceRecognitionEvent(modelHandle);
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        return mDelegate.getModelParameter(modelHandle, param);
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        mDelegate.setModelParameter(modelHandle, param, value);
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        return mDelegate.queryParameter(modelHandle, param);
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        return mDelegate.interfaceDescriptor();
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
new file mode 100644
index 0000000..6870f4f
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -0,0 +1,305 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
+ * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
+ * common HAL malfunctions, to help track them and assist in debugging.
+ *
+ * The class is thread-safe.
+ */
+public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
+    private static final String TAG = "SoundTriggerHalEnforcer";
+
+    /** The state of a model. */
+    private enum ModelState {
+        /** Model is loaded, but inactive. */
+        INACTIVE,
+        /** Model is active. */
+        ACTIVE,
+        /** A request to stop is being made, which may or may not have been processed yet. */
+        PENDING_STOP,
+    }
+
+    private final ISoundTriggerHal mUnderlying;
+    private final Map<Integer, ModelState> mModelStates = new HashMap<>();
+
+    public SoundTriggerHalEnforcer(
+            ISoundTriggerHal underlying) {
+        mUnderlying = underlying;
+    }
+
+    @Override
+    public Properties getProperties() {
+        try {
+            return mUnderlying.getProperties();
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        try {
+            mUnderlying.registerCallback(callback);
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        try {
+            synchronized (mModelStates) {
+                int handle = mUnderlying.loadSoundModel(soundModel,
+                        new ModelCallbackEnforcer(callback));
+                mModelStates.put(handle, ModelState.INACTIVE);
+                return handle;
+            }
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+        try {
+            synchronized (mModelStates) {
+                int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+                        new ModelCallbackEnforcer(callback));
+                mModelStates.put(handle, ModelState.INACTIVE);
+                return handle;
+            }
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        try {
+            // This call into the HAL may block on callback processing, thus must be done outside
+            // of the critical section. After this call returns we are guaranteed to no longer be
+            // getting unload events for that model.
+            mUnderlying.unloadSoundModel(modelHandle);
+            synchronized (mModelStates) {
+                // At this point, the model may have already been removed by a HAL callback, but the
+                // remove() method is a no-op in this case, so thus safe.
+                mModelStates.remove(modelHandle);
+            }
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        try {
+            // This call into the HAL may block on callback processing, thus must be done outside
+            // of the critical section. After this call returns we are guaranteed to no longer be
+            // getting stop events for that model.
+            synchronized (mModelStates) {
+                mModelStates.replace(modelHandle, ModelState.PENDING_STOP);
+            }
+            mUnderlying.stopRecognition(modelHandle);
+            synchronized (mModelStates) {
+                // At this point, the model might have been preemptively unloaded, but replace()
+                // do nothing when the entry does not exist, so all good.
+                mModelStates.replace(modelHandle, ModelState.INACTIVE);
+            }
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        try {
+            synchronized (mModelStates) {
+                mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+                mModelStates.replace(modelHandle, ModelState.ACTIVE);
+            }
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        try {
+            mUnderlying.forceRecognitionEvent(modelHandle);
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        try {
+            return mUnderlying.getModelParameter(modelHandle, param);
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        try {
+            mUnderlying.setModelParameter(modelHandle, param, value);
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        try {
+            return mUnderlying.queryParameter(modelHandle, param);
+        } catch (RuntimeException e) {
+            throw handleException(e);
+        }
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        mUnderlying.linkToDeath(recipient);
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mUnderlying.unlinkToDeath(recipient);
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        return mUnderlying.interfaceDescriptor();
+    }
+
+    @Override
+    public void flushCallbacks() {
+        mUnderlying.flushCallbacks();
+    }
+
+    private RuntimeException handleException(RuntimeException e) {
+        if (e instanceof RecoverableException) {
+            throw e;
+        }
+        if (e.getCause() instanceof DeadObjectException) {
+            // Server is dead, no need to reboot.
+            Log.e(TAG, "HAL died");
+            throw new RecoverableException(Status.DEAD_OBJECT);
+        }
+        Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+        reboot();
+        throw e;
+    }
+
+    @Override
+    public void reboot() {
+        mUnderlying.reboot();
+    }
+
+    @Override
+    public void detach() {
+        mUnderlying.detach();
+    }
+
+    private class ModelCallbackEnforcer implements ModelCallback {
+        private final ModelCallback mUnderlying;
+
+        private ModelCallbackEnforcer(
+                ModelCallback underlying) {
+            mUnderlying = underlying;
+        }
+
+        @Override
+        public void recognitionCallback(int model, RecognitionEvent event) {
+            int status = event.status;
+
+            synchronized (mModelStates) {
+                ModelState state = mModelStates.get(model);
+                if (state == null || state == ModelState.INACTIVE) {
+                    Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+                    reboot();
+                    return;
+                }
+                if (status != RecognitionStatus.FORCED) {
+                    mModelStates.replace(model, ModelState.INACTIVE);
+                }
+            }
+            // Always invoke the delegate from outside the critical section.
+            mUnderlying.recognitionCallback(model, event);
+        }
+
+        @Override
+        public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+            int status = event.common.status;
+            synchronized (mModelStates) {
+                ModelState state = mModelStates.get(model);
+                if (state == null || state == ModelState.INACTIVE) {
+                    Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+                    reboot();
+                    return;
+                }
+                if (status != RecognitionStatus.FORCED) {
+                    mModelStates.replace(model, ModelState.INACTIVE);
+                }
+            }
+            // Always invoke the delegate from outside the critical section.
+            mUnderlying.phraseRecognitionCallback(model, event);
+        }
+
+        @Override
+        public void modelUnloaded(int modelHandle) {
+            synchronized (mModelStates) {
+                ModelState state = mModelStates.get(modelHandle);
+                if (state == null) {
+                    Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+                    reboot();
+                    return;
+                }
+
+                if (state == ModelState.ACTIVE) {
+                    Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+                    reboot();
+                    return;
+                }
+                mModelStates.remove(modelHandle);
+            }
+            // Always invoke the delegate from outside the critical section.
+            mUnderlying.modelUnloaded(modelHandle);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
new file mode 100644
index 0000000..7dd28e0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number
+ * of models supported by the HAL, for HAL implementations older than V2.4 that do not support
+ * rejection of model loading at the HAL layer.
+ * Since preemptive model unloading has been introduced in V2.4, it should never be used in
+ * conjunction with this class, hence we don't bother considering preemtive unloading when counting
+ * the number of currently loaded models.
+ */
+public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal {
+    private final @NonNull ISoundTriggerHal mDelegate;
+    private final int mMaxModels;
+
+    // This counter is used to enforce the maximum number of loaded models.
+    private int mNumLoadedModels = 0;
+
+    private GlobalCallback mGlobalCallback;
+
+    public SoundTriggerHalMaxModelLimiter(
+            ISoundTriggerHal delegate, int maxModels) {
+        mDelegate = delegate;
+        this.mMaxModels = maxModels;
+    }
+
+    @Override
+    public void reboot() {
+        mDelegate.reboot();
+    }
+
+    @Override
+    public void detach() {
+        mDelegate.detach();
+    }
+
+    @Override
+    public Properties getProperties() {
+        return mDelegate.getProperties();
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        mGlobalCallback = callback;
+        mDelegate.registerCallback(mGlobalCallback);
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        synchronized (this) {
+            if (mNumLoadedModels == mMaxModels) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            int result = mDelegate.loadSoundModel(soundModel, callback);
+            ++mNumLoadedModels;
+            return result;
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+            ModelCallback callback) {
+        synchronized (this) {
+            if (mNumLoadedModels == mMaxModels) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            int result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+            ++mNumLoadedModels;
+            return result;
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        boolean wasAtMaxCapacity;
+        synchronized (this) {
+            wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels;
+        }
+        try {
+            mDelegate.unloadSoundModel(modelHandle);
+        } catch (Exception e) {
+            synchronized (this) {
+                ++mNumLoadedModels;
+            }
+            throw e;
+        }
+        if (wasAtMaxCapacity) {
+            // It is legal to invoke callbacks from within unloadSoundModel().
+            // See README.md for details.
+            mGlobalCallback.onResourcesAvailable();
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        mDelegate.stopRecognition(modelHandle);
+    }
+
+    @Override
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        mDelegate.forceRecognitionEvent(modelHandle);
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        return mDelegate.getModelParameter(modelHandle, param);
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        mDelegate.setModelParameter(modelHandle, param, value);
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        return mDelegate.queryParameter(modelHandle, param);
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        mDelegate.linkToDeath(recipient);
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mDelegate.unlinkToDeath(recipient);
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        return mDelegate.interfaceDescriptor();
+    }
+
+    @Override
+    public void flushCallbacks() {
+        mDelegate.flushCallbacks();
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
new file mode 100644
index 0000000..5fe06ee
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -0,0 +1,180 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
+ * HAL whenever they expire.
+ */
+public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
+    private static final long TIMEOUT_MS = 3000;
+    private static final String TAG = "SoundTriggerHalWatchdog";
+
+    private final @NonNull ISoundTriggerHal mUnderlying;
+    private final @NonNull Timer mTimer;
+
+    public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
+        mUnderlying = Objects.requireNonNull(underlying);
+        mTimer = new Timer("SoundTriggerHalWatchdog");
+    }
+
+    @Override
+    public Properties getProperties() {
+        try (Watchdog ignore = new Watchdog()) {
+            return mUnderlying.getProperties();
+        }
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.registerCallback(callback);
+        }
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        try (Watchdog ignore = new Watchdog()) {
+            return mUnderlying.loadSoundModel(soundModel, callback);
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+            ModelCallback callback) {
+        try (Watchdog ignore = new Watchdog()) {
+            return mUnderlying.loadPhraseSoundModel(soundModel, callback);
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.unloadSoundModel(modelHandle);
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.stopRecognition(modelHandle);
+        }
+    }
+
+    @Override
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+        }
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.forceRecognitionEvent(modelHandle);
+        }
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        try (Watchdog ignore = new Watchdog()) {
+            return mUnderlying.getModelParameter(modelHandle, param);
+        }
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        try (Watchdog ignore = new Watchdog()) {
+            mUnderlying.setModelParameter(modelHandle, param, value);
+        }
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        try (Watchdog ignore = new Watchdog()) {
+            return mUnderlying.queryParameter(modelHandle, param);
+        }
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        mUnderlying.linkToDeath(recipient);
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mUnderlying.unlinkToDeath(recipient);
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        return mUnderlying.interfaceDescriptor();
+    }
+
+    @Override
+    public void flushCallbacks() {
+        mUnderlying.flushCallbacks();
+    }
+
+    @Override
+    public void reboot() {
+        mUnderlying.reboot();
+    }
+
+    @Override
+    public void detach() {
+        mUnderlying.detach();
+    }
+
+    private class Watchdog implements AutoCloseable {
+        private final @NonNull
+        TimerTask mTask;
+        // This exception is used merely for capturing a stack trace at the time of creation.
+        private final @NonNull
+        Exception mException = new Exception();
+
+        Watchdog() {
+            mTask = new TimerTask() {
+                @Override
+                public void run() {
+                    Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+                    reboot();
+                }
+            };
+            mTimer.schedule(mTask, TIMEOUT_MS);
+        }
+
+        @Override
+        public void close() {
+            mTask.cancel();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 2f087f4..7a1f775 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -18,60 +18,116 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.Status;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.RemoteException;
+import android.system.OsConstants;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * An implementation of {@link ISoundTriggerHw2}, on top of any
+ * An implementation of {@link ISoundTriggerHal}, on top of any
  * android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
  * the details involved with retaining backward compatibility and adapts to the more pleasant syntax
- * exposed by {@link ISoundTriggerHw2}, compared to the bare driver interface.
+ * exposed by {@link ISoundTriggerHal}, compared to the bare driver interface.
  * <p>
  * Exception handling:
  * <ul>
  * <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
  * <li>All HAL malfunctions get thrown as {@link HalException}.
  * <li>All unsupported operations get thrown as {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED}
  * code.
  * </ul>
  */
-final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
-    private final @NonNull
-    IHwBinder mBinder;
-    private final @NonNull
-    android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
-    private final @Nullable
-    android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
-    private final @Nullable
-    android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
-    private final @Nullable
-    android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+    private final @NonNull Runnable mRebootRunnable;
+    private final @NonNull IHwBinder mBinder;
+    private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
+    private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
+    private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
+    private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+    private @Nullable android.hardware.soundtrigger.V2_4.ISoundTriggerHw mUnderlying_2_4;
 
-    public SoundTriggerHw2Compat(
-            @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw underlying) {
-        this(underlying.asBinder());
+    // HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
+    // passed on load and then pass it on start. We don't bother storing the callback on newer
+    // versions.
+    private final @NonNull ConcurrentMap<Integer, ModelCallback> mModelCallbacks =
+            new ConcurrentHashMap<>();
+
+    // A map from IBinder.DeathRecipient to IHwBinder.DeathRecipient for doing the mapping upon
+    // unlinking.
+    private final @NonNull Map<IBinder.DeathRecipient, IHwBinder.DeathRecipient>
+            mDeathRecipientMap = new HashMap<>();
+
+    // The properties are read at construction time and cached, since we need to use some of them
+    // to enforce constraints.
+    private final @NonNull Properties mProperties;
+
+    static ISoundTriggerHal create(
+            @NonNull ISoundTriggerHw underlying,
+            @NonNull Runnable rebootRunnable,
+            ICaptureStateNotifier notifier) {
+        return create(underlying.asBinder(), rebootRunnable, notifier);
     }
 
-    public SoundTriggerHw2Compat(IHwBinder binder) {
-        Objects.requireNonNull(binder);
+    static ISoundTriggerHal create(@NonNull IHwBinder binder,
+            @NonNull Runnable rebootRunnable,
+            ICaptureStateNotifier notifier) {
+        SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
+        ISoundTriggerHal result = compat;
+        // Add max model limiter for versions <2.4.
+        if (compat.mUnderlying_2_4 == null) {
+            result = new SoundTriggerHalMaxModelLimiter(result,
+                    compat.mProperties.maxSoundModels);
+        }
+        // Add concurrent capture handler for versions <2.4 which do not support concurrent capture.
+        if (compat.mUnderlying_2_4 == null && !compat.mProperties.concurrentCapture) {
+            result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
+        }
+        return result;
+    }
 
-        mBinder = binder;
+    private SoundTriggerHw2Compat(@NonNull IHwBinder binder, @NonNull Runnable rebootRunnable) {
+        mRebootRunnable = Objects.requireNonNull(rebootRunnable);
+        mBinder = Objects.requireNonNull(binder);
+        initUnderlying(binder);
+        mProperties = Objects.requireNonNull(getPropertiesInternal());
+    }
 
+    private void initUnderlying(IHwBinder binder) {
         // We want to share the proxy instances rather than create a separate proxy for every
         // version, so we go down the versions in descending order to find the latest one supported,
         // and then simply up-cast it to obtain all the versions that are earlier.
 
+        // Attempt 2.4
+        android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4 =
+                android.hardware.soundtrigger.V2_4.ISoundTriggerHw.asInterface(binder);
+        if (as2_4 != null) {
+            mUnderlying_2_0 =
+                    mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = as2_4;
+            return;
+        }
+
         // Attempt 2.3
         android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
                 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
         if (as2_3 != null) {
             mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
+            mUnderlying_2_4 = null;
             return;
         }
 
@@ -80,7 +136,7 @@
                 android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
         if (as2_2 != null) {
             mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
-            mUnderlying_2_3 = null;
+            mUnderlying_2_3 = mUnderlying_2_4 = null;
             return;
         }
 
@@ -89,7 +145,7 @@
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
         if (as2_1 != null) {
             mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
-            mUnderlying_2_2 = mUnderlying_2_3 = null;
+            mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
             return;
         }
 
@@ -98,7 +154,7 @@
                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
         if (as2_0 != null) {
             mUnderlying_2_0 = as2_0;
-            mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
+            mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
             return;
         }
 
@@ -111,12 +167,32 @@
         }
     }
 
+    private static void handleHalStatusAllowBusy(int status, String methodName) {
+        if (status == -OsConstants.EBUSY) {
+            throw new RecoverableException(Status.RESOURCE_CONTENTION);
+        }
+        handleHalStatus(status, methodName);
+    }
+
     @Override
-    public android.hardware.soundtrigger.V2_3.Properties getProperties() {
+    public void reboot() {
+        mRebootRunnable.run();
+    }
+
+    @Override
+    public void detach() {
+        // No-op.
+    }
+
+    @Override
+    public Properties getProperties() {
+        return mProperties;
+    }
+
+    private Properties getPropertiesInternal() {
         try {
             AtomicInteger retval = new AtomicInteger(-1);
-            AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
-                    properties =
+            AtomicReference<android.hardware.soundtrigger.V2_3.Properties> properties =
                     new AtomicReference<>();
             try {
                 as2_3().getProperties_2_3(
@@ -129,30 +205,57 @@
                 return getProperties_2_0();
             }
             handleHalStatus(retval.get(), "getProperties_2_3");
-            return properties.get();
+            return ConversionUtil.hidl2aidlProperties(properties.get());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     @Override
-    public int loadSoundModel(
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
-            Callback callback, int cookie) {
+    public void registerCallback(GlobalCallback callback) {
+        try {
+            try {
+                as2_4().registerGlobalCallback(new GlobalCallbackWrapper(callback));
+            } catch (NotSupported e) {
+                // In versions < 2.4 the events represented by this callback don't exist, we can
+                // safely ignore this.
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw.SoundModel hidlModel =
+                ConversionUtil.aidl2hidlSoundModel(soundModel);
         try {
             AtomicInteger retval = new AtomicInteger(-1);
             AtomicInteger handle = new AtomicInteger(0);
+
             try {
-                as2_1().loadSoundModel_2_1(soundModel, new SoundTriggerCallback(callback), cookie,
+                as2_4().loadSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
                         (r, h) -> {
                             retval.set(r);
                             handle.set(h);
                         });
+                handleHalStatusAllowBusy(retval.get(), "loadSoundModel_2_4");
             } catch (NotSupported e) {
-                // Fall-back to the 2.0 version:
-                return loadSoundModel_2_0(soundModel, callback, cookie);
+                // Fall-back to the 2.1 version:
+                try {
+                    as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+                            0,
+                            (r, h) -> {
+                                retval.set(r);
+                                handle.set(h);
+                            });
+                    handleHalStatus(retval.get(), "loadSoundModel_2_1");
+                    mModelCallbacks.put(handle.get(), callback);
+                } catch (NotSupported ee) {
+                    // Fall-back to the 2.0 version:
+                    return loadSoundModel_2_0(hidlModel, callback);
+                }
             }
-            handleHalStatus(retval.get(), "loadSoundModel_2_1");
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -160,24 +263,35 @@
     }
 
     @Override
-    public int loadPhraseSoundModel(
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
-            Callback callback, int cookie) {
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
+                ConversionUtil.aidl2hidlPhraseSoundModel(soundModel);
         try {
             AtomicInteger retval = new AtomicInteger(-1);
             AtomicInteger handle = new AtomicInteger(0);
             try {
-                as2_1().loadPhraseSoundModel_2_1(soundModel, new SoundTriggerCallback(callback),
-                        cookie,
+                as2_4().loadPhraseSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
                         (r, h) -> {
                             retval.set(r);
                             handle.set(h);
                         });
+                handleHalStatusAllowBusy(retval.get(), "loadPhraseSoundModel_2_4");
             } catch (NotSupported e) {
-                // Fall-back to the 2.0 version:
-                return loadPhraseSoundModel_2_0(soundModel, callback, cookie);
+                // Fall-back to the 2.1 version:
+                try {
+                    as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+                            0,
+                            (r, h) -> {
+                                retval.set(r);
+                                handle.set(h);
+                            });
+                    handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
+                    mModelCallbacks.put(handle.get(), callback);
+                } catch (NotSupported ee) {
+                    // Fall-back to the 2.0 version:
+                    return loadPhraseSoundModel_2_0(hidlModel, callback);
+                }
             }
-            handleHalStatus(retval.get(), "loadSoundModel_2_1");
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -187,6 +301,8 @@
     @Override
     public void unloadSoundModel(int modelHandle) {
         try {
+            // Safe if key doesn't exist.
+            mModelCallbacks.remove(modelHandle);
             int retval = as2_0().unloadSoundModel(modelHandle);
             handleHalStatus(retval, "unloadSoundModel");
         } catch (RemoteException e) {
@@ -206,26 +322,23 @@
     }
 
     @Override
-    public void stopAllRecognitions() {
-        try {
-            int retval = as2_0().stopAllRecognitions();
-            handleHalStatus(retval, "stopAllRecognitions");
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
-    @Override
-    public void startRecognition(int modelHandle,
-            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
-            Callback callback, int cookie) {
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
+                ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
         try {
             try {
-                int retval = as2_3().startRecognition_2_3(modelHandle, config);
-                handleHalStatus(retval, "startRecognition_2_3");
+                int retval = as2_4().startRecognition_2_4(modelHandle, hidlConfig);
+                handleHalStatusAllowBusy(retval, "startRecognition_2_4");
             } catch (NotSupported e) {
-                // Fall-back to the 2.0 version:
-                startRecognition_2_1(modelHandle, config, callback, cookie);
+                // Fall-back to the 2.3 version:
+                try {
+                    int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
+                    handleHalStatus(retval, "startRecognition_2_3");
+                } catch (NotSupported ee) {
+                    // Fall-back to the 2.0 version:
+                    startRecognition_2_1(modelHandle, hidlConfig);
+                }
             }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -233,7 +346,7 @@
     }
 
     @Override
-    public void getModelState(int modelHandle) {
+    public void forceRecognitionEvent(int modelHandle) {
         try {
             int retval = as2_2().getModelState(modelHandle);
             handleHalStatus(retval, "getModelState");
@@ -276,8 +389,7 @@
     }
 
     @Override
-    public android.hardware.soundtrigger.V2_3.ModelParameterRange queryParameter(int modelHandle,
-            int param) {
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
         AtomicInteger status = new AtomicInteger(-1);
         AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
                 optionalRange =
@@ -298,25 +410,36 @@
         return (optionalRange.get().getDiscriminator()
                 == android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
                 ?
-                optionalRange.get().range() : null;
+                ConversionUtil.hidl2aidlModelParameterRange(optionalRange.get().range()) : null;
     }
 
     @Override
-    public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
-        return mBinder.linkToDeath(recipient, cookie);
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        IHwBinder.DeathRecipient wrapper = cookie -> recipient.binderDied();
+        mDeathRecipientMap.put(recipient, wrapper);
+        mBinder.linkToDeath(wrapper, 0);
     }
 
     @Override
-    public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
-        return mBinder.unlinkToDeath(recipient);
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mBinder.unlinkToDeath(mDeathRecipientMap.remove(recipient));
     }
 
     @Override
-    public String interfaceDescriptor() throws RemoteException {
-        return as2_0().interfaceDescriptor();
+    public String interfaceDescriptor() {
+        try {
+            return as2_0().interfaceDescriptor();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
     }
 
-    private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+    @Override
+    public void flushCallbacks() {
+        // This is a no-op. Only implemented for decorators.
+    }
+
+    private Properties getProperties_2_0()
             throws RemoteException {
         AtomicInteger retval = new AtomicInteger(-1);
         AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
@@ -328,12 +451,13 @@
                     properties.set(p);
                 });
         handleHalStatus(retval.get(), "getProperties");
-        return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+        return ConversionUtil.hidl2aidlProperties(
+                Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get()));
     }
 
     private int loadSoundModel_2_0(
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
-            Callback callback, int cookie)
+            ModelCallback callback)
             throws RemoteException {
         // Convert the soundModel to V2.0.
         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
@@ -341,17 +465,18 @@
 
         AtomicInteger retval = new AtomicInteger(-1);
         AtomicInteger handle = new AtomicInteger(0);
-        as2_0().loadSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie, (r, h) -> {
+        as2_0().loadSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0, (r, h) -> {
             retval.set(r);
             handle.set(h);
         });
         handleHalStatus(retval.get(), "loadSoundModel");
+        mModelCallbacks.put(handle.get(), callback);
         return handle.get();
     }
 
     private int loadPhraseSoundModel_2_0(
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
-            Callback callback, int cookie)
+            ModelCallback callback)
             throws RemoteException {
         // Convert the soundModel to V2.0.
         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
@@ -359,28 +484,28 @@
 
         AtomicInteger retval = new AtomicInteger(-1);
         AtomicInteger handle = new AtomicInteger(0);
-        as2_0().loadPhraseSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie,
+        as2_0().loadPhraseSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0,
                 (r, h) -> {
                     retval.set(r);
                     handle.set(h);
                 });
         handleHalStatus(retval.get(), "loadSoundModel");
+        mModelCallbacks.put(handle.get(), callback);
         return handle.get();
     }
 
     private void startRecognition_2_1(int modelHandle,
-            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
-            Callback callback, int cookie) {
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
         try {
             try {
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
                         Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
                 int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
-                        new SoundTriggerCallback(callback), cookie);
+                        new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
                 handleHalStatus(retval, "startRecognition_2_1");
             } catch (NotSupported e) {
                 // Fall-back to the 2.0 version:
-                startRecognition_2_0(modelHandle, config, callback, cookie);
+                startRecognition_2_0(modelHandle, config);
             }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -388,13 +513,12 @@
     }
 
     private void startRecognition_2_0(int modelHandle,
-            android.hardware.soundtrigger.V2_3.RecognitionConfig config,
-            Callback callback, int cookie)
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config)
             throws RemoteException {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
                 Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
         int retval = as2_0().startRecognition(modelHandle, config_2_0,
-                new SoundTriggerCallback(callback), cookie);
+                new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
         handleHalStatus(retval, "startRecognition");
     }
 
@@ -427,6 +551,14 @@
         return mUnderlying_2_3;
     }
 
+    private @NonNull
+    android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4() throws NotSupported {
+        if (mUnderlying_2_4 == null) {
+            throw new NotSupported("Underlying driver version < 2.4");
+        }
+        return mUnderlying_2_4;
+    }
+
     /**
      * A checked exception representing the requested interface version not being supported.
      * At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
@@ -448,28 +580,48 @@
         }
     }
 
-    private static class SoundTriggerCallback extends
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
-        private final @NonNull
-        Callback mDelegate;
+    private static class GlobalCallbackWrapper extends
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.Stub {
+        private final @NonNull GlobalCallback mDelegate;
 
-        private SoundTriggerCallback(
-                @NonNull Callback delegate) {
+        private GlobalCallbackWrapper(@NonNull GlobalCallback delegate) {
+            mDelegate = delegate;
+        }
+
+        @Override
+        public void onResourcesAvailable() {
+            mDelegate.onResourcesAvailable();
+        }
+    }
+
+    private static class ModelCallbackWrapper extends
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.Stub {
+        private final @NonNull ModelCallback mDelegate;
+
+        private ModelCallbackWrapper(
+                @NonNull ModelCallback delegate) {
             mDelegate = Objects.requireNonNull(delegate);
         }
 
         @Override
+        public void modelUnloaded(int modelHandle) {
+            mDelegate.modelUnloaded(modelHandle);
+        }
+
+        @Override
         public void recognitionCallback_2_1(
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
                 int cookie) {
-            mDelegate.recognitionCallback(event, cookie);
+            mDelegate.recognitionCallback(event.header.model,
+                    ConversionUtil.hidl2aidlRecognitionEvent(event));
         }
 
         @Override
         public void phraseRecognitionCallback_2_1(
                 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
                 int cookie) {
-            mDelegate.phraseRecognitionCallback(event, cookie);
+            mDelegate.phraseRecognitionCallback(event.common.header.model,
+                    ConversionUtil.hidl2aidlPhraseRecognitionEvent(event));
         }
 
         @Override
@@ -485,7 +637,7 @@
                 int cookie) {
             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
                     Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
-            mDelegate.recognitionCallback(event_2_1, cookie);
+            recognitionCallback_2_1(event_2_1, cookie);
         }
 
         @Override
@@ -494,7 +646,7 @@
                 int cookie) {
             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
                     event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
-            mDelegate.phraseRecognitionCallback(event_2_1, cookie);
+            phraseRecognitionCallback_2_1(event_2_1, cookie);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
deleted file mode 100644
index cf7460b..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ /dev/null
@@ -1,257 +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.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.media.soundtrigger_middleware.Status;
-import android.os.DeadObjectException;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
- * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
- * common HAL malfunctions, to help track them and assist in debugging.
- *
- * The class is thread-safe.
- */
-public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
-    static final String TAG = "SoundTriggerHw2Enforcer";
-
-    final ISoundTriggerHw2 mUnderlying;
-    Map<Integer, Boolean> mModelStates = new HashMap<>();
-
-    public SoundTriggerHw2Enforcer(
-            ISoundTriggerHw2 underlying) {
-        mUnderlying = underlying;
-    }
-
-    @Override
-    public Properties getProperties() {
-        try {
-            return mUnderlying.getProperties();
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
-            int cookie) {
-        try {
-            int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
-                    cookie);
-            synchronized (mModelStates) {
-                mModelStates.put(handle, false);
-            }
-            return handle;
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
-            int cookie) {
-        try {
-            int handle = mUnderlying.loadPhraseSoundModel(soundModel,
-                    new CallbackEnforcer(callback),
-                    cookie);
-            synchronized (mModelStates) {
-                mModelStates.put(handle, false);
-            }
-            return handle;
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void unloadSoundModel(int modelHandle) {
-        try {
-            mUnderlying.unloadSoundModel(modelHandle);
-            synchronized (mModelStates) {
-                mModelStates.remove(modelHandle);
-            }
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void stopRecognition(int modelHandle) {
-        try {
-            mUnderlying.stopRecognition(modelHandle);
-            synchronized (mModelStates) {
-                mModelStates.replace(modelHandle, false);
-            }
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void stopAllRecognitions() {
-        try {
-            mUnderlying.stopAllRecognitions();
-            synchronized (mModelStates) {
-                for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
-                    entry.setValue(false);
-                }
-            }
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
-            int cookie) {
-        // It is possible that an event will be sent before the HAL returns from the
-        // startRecognition call, thus it is important to set the state to active before the call.
-        synchronized (mModelStates) {
-            mModelStates.replace(modelHandle, true);
-        }
-        try {
-            mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
-                    cookie);
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void getModelState(int modelHandle) {
-        try {
-            mUnderlying.getModelState(modelHandle);
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public int getModelParameter(int modelHandle, int param) {
-        try {
-            return mUnderlying.getModelParameter(modelHandle, param);
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public void setModelParameter(int modelHandle, int param, int value) {
-        try {
-            mUnderlying.setModelParameter(modelHandle, param, value);
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public ModelParameterRange queryParameter(int modelHandle, int param) {
-        try {
-            return mUnderlying.queryParameter(modelHandle, param);
-        } catch (RuntimeException e) {
-            throw handleException(e);
-        }
-    }
-
-    @Override
-    public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
-        return mUnderlying.linkToDeath(recipient, cookie);
-    }
-
-    @Override
-    public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
-        return mUnderlying.unlinkToDeath(recipient);
-    }
-
-    @Override
-    public String interfaceDescriptor() throws RemoteException {
-        return mUnderlying.interfaceDescriptor();
-    }
-
-    private static RuntimeException handleException(RuntimeException e) {
-        if (e.getCause() instanceof DeadObjectException) {
-            // Server is dead, no need to reboot.
-            Log.e(TAG, "HAL died");
-            throw new RecoverableException(Status.DEAD_OBJECT);
-        }
-        Log.e(TAG, "Exception caught from HAL, rebooting HAL");
-        rebootHal();
-        throw e;
-    }
-
-    private static void rebootHal() {
-        // This property needs to be defined in an init.rc script and trigger a HAL reboot.
-        SystemProperties.set("sys.audio.restart.hal", "1");
-    }
-
-    private class CallbackEnforcer implements Callback {
-        private final Callback mUnderlying;
-
-        private CallbackEnforcer(
-                Callback underlying) {
-            mUnderlying = underlying;
-        }
-
-        @Override
-        public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
-                int cookie) {
-            int model = event.header.model;
-            synchronized (mModelStates) {
-                if (!mModelStates.getOrDefault(model, false)) {
-                    Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
-                    rebootHal();
-                    return;
-                }
-                if (event.header.status
-                        != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
-                    mModelStates.replace(model, false);
-                }
-            }
-            mUnderlying.recognitionCallback(event, cookie);
-        }
-
-        @Override
-        public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
-                int cookie) {
-            int model = event.common.header.model;
-            synchronized (mModelStates) {
-                if (!mModelStates.getOrDefault(model, false)) {
-                    Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
-                    rebootHal();
-                    return;
-                }
-                if (event.common.header.status
-                        != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
-                    mModelStates.replace(model, false);
-                }
-            }
-            mUnderlying.phraseRecognitionCallback(event, cookie);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
deleted file mode 100644
index 212f81f..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ /dev/null
@@ -1,174 +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.soundtrigger_middleware;
-
-import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.util.Objects;
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
- * HAL whenever they expire.
- */
-public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
-    private static final long TIMEOUT_MS = 3000;
-    private static final String TAG = "SoundTriggerHw2Watchdog";
-
-    private final @NonNull
-    ISoundTriggerHw2 mUnderlying;
-    private final @NonNull
-    Timer mTimer;
-
-    public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
-        mUnderlying = Objects.requireNonNull(underlying);
-        mTimer = new Timer("SoundTriggerHw2Watchdog");
-    }
-
-    @Override
-    public Properties getProperties() {
-        try (Watchdog ignore = new Watchdog()) {
-            return mUnderlying.getProperties();
-        }
-    }
-
-    @Override
-    public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
-            int cookie) {
-        try (Watchdog ignore = new Watchdog()) {
-            return mUnderlying.loadSoundModel(soundModel, callback, cookie);
-        }
-    }
-
-    @Override
-    public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
-            int cookie) {
-        try (Watchdog ignore = new Watchdog()) {
-            return mUnderlying.loadPhraseSoundModel(soundModel, callback, cookie);
-        }
-    }
-
-    @Override
-    public void unloadSoundModel(int modelHandle) {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.unloadSoundModel(modelHandle);
-        }
-    }
-
-    @Override
-    public void stopRecognition(int modelHandle) {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.stopRecognition(modelHandle);
-        }
-    }
-
-    @Override
-    public void stopAllRecognitions() {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.stopAllRecognitions();
-        }
-    }
-
-    @Override
-    public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
-            int cookie) {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.startRecognition(modelHandle, config, callback, cookie);
-        }
-    }
-
-    @Override
-    public void getModelState(int modelHandle) {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.getModelState(modelHandle);
-        }
-    }
-
-    @Override
-    public int getModelParameter(int modelHandle, int param) {
-        try (Watchdog ignore = new Watchdog()) {
-            return mUnderlying.getModelParameter(modelHandle, param);
-        }
-    }
-
-    @Override
-    public void setModelParameter(int modelHandle, int param, int value) {
-        try (Watchdog ignore = new Watchdog()) {
-            mUnderlying.setModelParameter(modelHandle, param, value);
-        }
-    }
-
-    @Override
-    public ModelParameterRange queryParameter(int modelHandle, int param) {
-        try (Watchdog ignore = new Watchdog()) {
-            return mUnderlying.queryParameter(modelHandle, param);
-        }
-    }
-
-    @Override
-    public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
-        return mUnderlying.linkToDeath(recipient, cookie);
-    }
-
-    @Override
-    public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
-        return mUnderlying.unlinkToDeath(recipient);
-    }
-
-    @Override
-    public String interfaceDescriptor() throws RemoteException {
-        return mUnderlying.interfaceDescriptor();
-    }
-
-    private static void rebootHal() {
-        // This property needs to be defined in an init.rc script and trigger a HAL reboot.
-        SystemProperties.set("sys.audio.restart.hal", "1");
-    }
-
-    private class Watchdog implements AutoCloseable {
-        private final @NonNull
-        TimerTask mTask;
-        // This exception is used merely for capturing a stack trace at the time of creation.
-        private final @NonNull
-        Exception mException = new Exception();
-
-        Watchdog() {
-            mTask = new TimerTask() {
-                @Override
-                public void run() {
-                    Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
-                    rebootHal();
-                }
-            };
-            mTimer.schedule(mTask, TIMEOUT_MS);
-        }
-
-        @Override
-        public void close() {
-            mTask.cancel();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
new file mode 100644
index 0000000..f564756
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+public class SoundTriggerHw3Compat implements ISoundTriggerHal {
+    private final @NonNull ISoundTriggerHw mDriver;
+    private final @NonNull Runnable mRebootRunnable;
+
+    public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) {
+        mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder);
+        mRebootRunnable = rebootRunnable;
+    }
+
+    @Override
+    public Properties getProperties() {
+        try {
+            return mDriver.getProperties();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        try {
+            mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback));
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        try {
+            return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback));
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == Status.RESOURCE_CONTENTION) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+        try {
+            return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback));
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == Status.RESOURCE_CONTENTION) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        try {
+            mDriver.unloadSoundModel(modelHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+            RecognitionConfig config) {
+        try {
+            mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == Status.RESOURCE_CONTENTION) {
+                throw new RecoverableException(Status.RESOURCE_CONTENTION);
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        try {
+            mDriver.stopRecognition(modelHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        try {
+            mDriver.forceRecognitionEvent(modelHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        try {
+            return mDriver.queryParameter(modelHandle, param);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        try {
+            return mDriver.getParameter(modelHandle, param);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        try {
+            mDriver.setParameter(modelHandle, param, value);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        try {
+            return mDriver.asBinder().getInterfaceDescriptor();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        try {
+            mDriver.asBinder().linkToDeath(recipient, 0);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mDriver.asBinder().unlinkToDeath(recipient, 0);
+    }
+
+    @Override
+    public void flushCallbacks() {
+        // No-op.
+    }
+
+    @Override
+    public void reboot() {
+        mRebootRunnable.run();
+    }
+
+    @Override
+    public void detach() {
+        // No-op.
+    }
+
+    private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub {
+        private final @NonNull GlobalCallback mDelegate;
+
+        public GlobalCallbackAdaper(@NonNull GlobalCallback callback) {
+            mDelegate = callback;
+        }
+
+        @Override
+        public void onResourcesAvailable() {
+            mDelegate.onResourcesAvailable();
+        }
+    }
+
+    private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub {
+        private final @NonNull ModelCallback mDelegate;
+
+        public ModelCallbackAdaper(ModelCallback callback) {
+            mDelegate = callback;
+        }
+
+        @Override
+        public void modelUnloaded(int model) {
+            mDelegate.modelUnloaded(model);
+        }
+
+        @Override
+        public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+            mDelegate.phraseRecognitionCallback(model, event);
+        }
+
+        @Override
+        public void recognitionCallback(int model, RecognitionEvent event) {
+            mDelegate.recognitionCallback(model, event);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index d76b1bf..c8c0f3d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -17,12 +17,9 @@
 package com.android.server.soundtrigger_middleware;
 
 import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.IBinder;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -39,7 +36,7 @@
  * <li>There is no binder instance associated with this implementation. Do not call asBinder().
  * <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
  * recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status}
+ * {@link android.media.soundtrigger.Status}
  * constants. Any other exception thrown should be regarded as a bug in the implementation or one
  * of its dependencies (assuming correct usage).
  * <li>The implementation is designed for testibility by featuring dependency injection (the
@@ -84,15 +81,15 @@
             @NonNull AudioSessionProvider audioSessionProvider) {
         List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
 
-        for (int i = 0; i < halFactories.length; ++i) {
+        for (HalFactory halFactory : halFactories) {
             try {
-                modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
+                modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
             } catch (Exception e) {
                 Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
             }
         }
 
-        mModules = modules.toArray(new SoundTriggerModule[modules.size()]);
+        mModules = modules.toArray(new SoundTriggerModule[0]);
     }
 
     /**
@@ -122,18 +119,4 @@
     ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
         return mModules[handle].attach(callback);
     }
-
-    @Override
-    public void setCaptureState(boolean active) {
-        for (SoundTriggerModule module : mModules) {
-            module.setExternalCaptureState(active);
-        }
-    }
-
-    @Override
-    public @NonNull
-    IBinder asBinder() {
-        throw new UnsupportedOperationException(
-                "This implementation is not inteded to be used directly with Binder.");
-    }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2ef0759..559e777 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,16 +18,16 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
 import android.media.permission.Identity;
 import android.media.permission.IdentityContext;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -57,9 +57,10 @@
  *     }
  * }
  * </code></pre>
- * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
- * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
- * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ * The actual handling of these events is then done inside of
+ * {@link #logReturnWithObject(Object, Identity, String, Object, Object[])},
+ * {@link #logVoidReturnWithObject(Object, Identity, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, Identity, String, Exception, Object[])}.
  */
 public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable {
     private static final String TAG = "SoundTriggerMiddlewareLogging";
@@ -96,23 +97,6 @@
         }
     }
 
-    @Override
-    public void setCaptureState(boolean active) throws RemoteException {
-        try {
-            mDelegate.setCaptureState(active);
-            logVoidReturn("setCaptureState", active);
-        } catch (Exception e) {
-            logException("setCaptureState", e, active);
-            throw e;
-        }
-    }
-
-    @Override
-    public IBinder asBinder() {
-        throw new UnsupportedOperationException(
-                "This implementation is not inteded to be used directly with Binder.");
-    }
-
     // Override toString() in order to have the delegate's ID in it.
     @Override
     public String toString() {
@@ -298,10 +282,10 @@
             }
 
             @Override
-            public void onRecognition(int modelHandle, RecognitionEvent event)
+            public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
                     throws RemoteException {
                 try {
-                    mCallbackDelegate.onRecognition(modelHandle, event);
+                    mCallbackDelegate.onRecognition(modelHandle, event, captureSession);
                     logVoidReturn("onRecognition", modelHandle, event);
                 } catch (Exception e) {
                     logException("onRecognition", e, modelHandle, event);
@@ -310,10 +294,11 @@
             }
 
             @Override
-            public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+            public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+                    int captureSession)
                     throws RemoteException {
                 try {
-                    mCallbackDelegate.onPhraseRecognition(modelHandle, event);
+                    mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
                     logVoidReturn("onPhraseRecognition", modelHandle, event);
                 } catch (Exception e) {
                     logException("onPhraseRecognition", e, modelHandle, event);
@@ -322,12 +307,23 @@
             }
 
             @Override
-            public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+            public void onModelUnloaded(int modelHandle) throws RemoteException {
                 try {
-                    mCallbackDelegate.onRecognitionAvailabilityChange(available);
-                    logVoidReturn("onRecognitionAvailabilityChange", available);
+                    mCallbackDelegate.onModelUnloaded(modelHandle);
+                    logVoidReturn("onModelUnloaded", modelHandle);
                 } catch (Exception e) {
-                    logException("onRecognitionAvailabilityChange", e, available);
+                    logException("onModelUnloaded", e, modelHandle);
+                    throw e;
+                }
+            }
+
+            @Override
+            public void onResourcesAvailable() throws RemoteException {
+                try {
+                    mCallbackDelegate.onResourcesAvailable();
+                    logVoidReturn("onResourcesAvailable");
+                } catch (Exception e) {
+                    logException("onResourcesAvailable", e);
                     throw 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 9999aff..76927e1 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -24,20 +24,20 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.PermissionChecker;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
 import android.media.permission.Identity;
 import android.media.permission.IdentityContext;
 import android.media.permission.PermissionUtil;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -90,24 +90,12 @@
         return wrapper.attach(mDelegate.attach(handle, wrapper.getCallbackWrapper()));
     }
 
-    @Override
-    public void setCaptureState(boolean active) throws RemoteException {
-        // This is an internal call. No permissions needed.
-        mDelegate.setCaptureState(active);
-    }
-
     // Override toString() in order to have the delegate's ID in it.
     @Override
     public String toString() {
         return Objects.toString(mDelegate);
     }
 
-    @Override
-    public IBinder asBinder() {
-        throw new UnsupportedOperationException(
-                "This implementation is not inteded to be used directly with Binder.");
-    }
-
     /**
      * Get the identity context, or throws an InternalServerError if it has not been established.
      *
@@ -305,22 +293,28 @@
             }
 
             @Override
-            public void onRecognition(int modelHandle, RecognitionEvent event)
+            public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
                     throws RemoteException {
                 enforcePermissions("Sound trigger recognition.");
-                mDelegate.onRecognition(modelHandle, event);
+                mDelegate.onRecognition(modelHandle, event, captureSession);
             }
 
             @Override
-            public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+            public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+                    int captureSession)
                     throws RemoteException {
                 enforcePermissions("Sound trigger phrase recognition.");
-                mDelegate.onPhraseRecognition(modelHandle, event);
+                mDelegate.onPhraseRecognition(modelHandle, event, captureSession);
             }
 
             @Override
-            public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
-                mDelegate.onRecognitionAvailabilityChange(available);
+            public void onResourcesAvailable() throws RemoteException {
+                mDelegate.onResourcesAvailable();
+            }
+
+            @Override
+            public void onModelUnloaded(int modelHandle) throws RemoteException {
+                mDelegate.onModelUnloaded(modelHandle);
             }
 
             @Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index db7a575..1995e54 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,7 +20,10 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.Identity;
 import android.media.permission.PermissionUtil;
@@ -28,13 +31,8 @@
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.os.RemoteException;
-import android.util.Log;
 
 import com.android.server.SystemService;
 
@@ -79,13 +77,6 @@
             @NonNull Context context) {
         mDelegate = Objects.requireNonNull(delegate);
         mContext = context;
-        new ExternalCaptureStateTracker(active -> {
-            try {
-                mDelegate.setCaptureState(active);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            }
-        });
     }
 
     @Override
@@ -232,23 +223,15 @@
 
         @Override
         public void onStart() {
-            HalFactory[] factories = new HalFactory[]{() -> {
-                try {
-                    Log.d(TAG, "Connecting to default ISoundTriggerHw");
-                    return ISoundTriggerHw.getService(true);
-                } catch (RemoteException e) {
-                    throw e.rethrowAsRuntimeException();
-                }
-            }};
+            HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()};
 
             publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
-                    new SoundTriggerMiddlewareService(
-                            new SoundTriggerMiddlewareLogging(
-                                    new SoundTriggerMiddlewarePermission(
-                                            new SoundTriggerMiddlewareValidation(
-                                                    new SoundTriggerMiddlewareImpl(factories,
-                                                            new AudioSessionProviderImpl())),
-                                            getContext())), getContext()));
+                    new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging(
+                            new SoundTriggerMiddlewarePermission(
+                                    new SoundTriggerMiddlewareValidation(
+                                            new SoundTriggerMiddlewareImpl(factories,
+                                                    new AudioSessionProviderImpl())),
+                                    getContext())), getContext()));
         }
     }
 }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 458eae9..7b31946 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,21 +18,21 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
 import android.media.permission.Identity;
 import android.media.permission.IdentityContext;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -46,10 +46,6 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces correct usage by
@@ -89,7 +85,8 @@
  * }
  * </pre></code>
  * Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
+ * with client-server separation. Notable exceptions are stopRecognition() and unloadModel(), which
+ * follow slightly more complicated rules for synchronization (see README.md for details).
  * <p>
  * <b>Exception handling approach:</b><br>
  * We make sure all client faults (argument and state validation) happen first, and
@@ -115,21 +112,18 @@
     }
 
     private class ModuleState {
-        final @NonNull SoundTriggerModuleProperties properties;
-        Set<Session> sessions = new HashSet<>();
+        public @NonNull Properties properties;
+        public Set<Session> sessions = new HashSet<>();
 
-        private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
+        private ModuleState(@NonNull Properties properties) {
             this.properties = properties;
         }
     }
 
-    private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
-
     private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
     private Map<Integer, ModuleState> mModules;
 
-    public SoundTriggerMiddlewareValidation(
-            @NonNull ISoundTriggerMiddlewareInternal delegate) {
+    public SoundTriggerMiddlewareValidation(@NonNull ISoundTriggerMiddlewareInternal delegate) {
         mDelegate = delegate;
     }
 
@@ -137,8 +131,8 @@
      * Generic exception handling for exceptions thrown by the underlying implementation.
      *
      * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
-     * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
-     * (<b>not</b> passed by Binder to the caller).
+     * by Binder to the caller) and <i>any other</i> exception as a {@link ServiceSpecificException}
+     * with a {@link Status#INTERNAL_ERROR} code.
      * <p>
      * Typical usage:
      * <code><pre>
@@ -149,8 +143,7 @@
      * }
      * </pre></code>
      */
-    static @NonNull
-    RuntimeException handleException(@NonNull Exception e) {
+    static @NonNull RuntimeException handleException(@NonNull Exception e) {
         if (e instanceof RecoverableException) {
             throw new ServiceSpecificException(((RecoverableException) e).errorCode,
                     e.getMessage());
@@ -161,8 +154,7 @@
     }
 
     @Override
-    public @NonNull
-    SoundTriggerModuleDescriptor[] listModules() {
+    public @NonNull SoundTriggerModuleDescriptor[] listModules() {
         // Input validation (always valid).
 
         synchronized (this) {
@@ -186,6 +178,7 @@
                             throw new RuntimeException(
                                     "listModules must always return the same result.");
                         }
+                        mModules.get(desc.handle).properties = desc.properties;
                     }
                 }
                 return result;
@@ -223,23 +216,6 @@
         }
     }
 
-    @Override
-    public void setCaptureState(boolean active) {
-        // This is an internal call. No permissions needed.
-        //
-        // Normally, we would acquire a lock here. However, we do not access any state here so it
-        // is safe to not lock. This call is typically done from a different context than all the
-        // other calls and may result in a deadlock if we lock here (between the audio server and
-        // the system server).
-        try {
-            mDelegate.setCaptureState(active);
-        } catch (Exception e) {
-            throw handleException(e);
-        } finally {
-            mCaptureState.set(active);
-        }
-    }
-
     // Override toString() in order to have the delegate's ID in it.
     @Override
     public String toString() {
@@ -247,17 +223,8 @@
     }
 
     @Override
-    public IBinder asBinder() {
-        throw new UnsupportedOperationException(
-                "This implementation is not inteded to be used directly with Binder.");
-    }
-
-    @Override
     public void dump(PrintWriter pw) {
         synchronized (this) {
-            Boolean captureState = mCaptureState.get();
-            pw.printf("Capture state is %s\n\n", captureState == null ? "uninitialized"
-                    : (captureState ? "active" : "inactive"));
             if (mModules != null) {
                 for (int handle : mModules.keySet()) {
                     final ModuleState module = mModules.get(handle);
@@ -303,10 +270,17 @@
              * delivered to the caller (most commonly, for permission reasons).
              */
             INTERCEPTED,
+            /**
+             * Model has been preemptively unloaded by the HAL.
+             */
+            PREEMPTED,
         }
 
         /** Activity state. */
-        private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
+        Activity activityState = Activity.LOADED;
+
+        /** Recognition config, used to start the model. */
+        RecognitionConfig config;
 
         /** Human-readable description of the model. */
         final String description;
@@ -316,9 +290,7 @@
          * parameter is supported. A null value means it is known to not be supported. A non-null
          * value indicates the valid value range.
          */
-        private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
-
-        private RecognitionConfig mConfig;
+        private final Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
 
         /**
          * Check that the given parameter is known to be supported for this model.
@@ -353,32 +325,6 @@
             Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
                     "value");
         }
-
-        /**
-         * Update support state for the given parameter for this model.
-         *
-         * @param modelParam The parameter key.
-         * @param range      The parameter value range, or null if not supported.
-         */
-        void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
-            parameterSupport.put(modelParam, range);
-        }
-
-        Activity getActivityState() {
-            return Activity.values()[mActivityState.get()];
-        }
-
-        void setActivityState(Activity activity) {
-            mActivityState.set(activity.ordinal());
-        }
-
-        void setRecognitionConfig(@NonNull RecognitionConfig config) {
-            mConfig = config;
-        }
-
-        RecognitionConfig getRecognitionConfig() {
-            return mConfig;
-        }
     }
 
     /**
@@ -387,13 +333,7 @@
      */
     private class Session extends ISoundTriggerModule.Stub {
         private ISoundTriggerModule mDelegate;
-        // While generally all the fields of this class must be changed under a lock, an exception
-        // is made for the specific case of changing a model state from ACTIVE to LOADED, which
-        // may happen as result of a recognition callback. This would happen atomically and is
-        // necessary in order to avoid deadlocks associated with locking from within callbacks
-        // possibly originating from the audio server.
-        private @NonNull
-        ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
+        private final @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
         private final int mHandle;
         private ModuleStatus mState = ModuleStatus.ALIVE;
         private final CallbackWrapper mCallbackWrapper;
@@ -461,7 +401,6 @@
         @Override
         public void unloadModel(int modelHandle) {
             // Input validation (always valid).
-
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
                 if (mState == ModuleStatus.DETACHED) {
@@ -472,18 +411,24 @@
                 if (modelState == null) {
                     throw new IllegalStateException("Invalid handle: " + modelHandle);
                 }
-                if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+                // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+                if (modelState.activityState != ModelState.Activity.LOADED
+                        && modelState.activityState != ModelState.Activity.PREEMPTED) {
                     throw new IllegalStateException("Model with handle: " + modelHandle
                             + " has invalid state for unloading");
                 }
+            }
 
-                // From here on, every exception isn't client's fault.
-                try {
-                    mDelegate.unloadModel(modelHandle);
-                    mLoadedModels.remove(modelHandle);
-                } catch (Exception e) {
-                    throw handleException(e);
-                }
+            // From here on, every exception isn't client's fault.
+            try {
+                // Calling the delegate must be done outside the lock.
+                mDelegate.unloadModel(modelHandle);
+            } catch (Exception e) {
+                throw handleException(e);
+            }
+
+            synchronized (SoundTriggerMiddlewareValidation.this) {
+                mLoadedModels.remove(modelHandle);
             }
         }
 
@@ -502,21 +447,20 @@
                 if (modelState == null) {
                     throw new IllegalStateException("Invalid handle: " + modelHandle);
                 }
-                if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+                ModelState.Activity activityState = modelState.activityState;
+                // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+                if (activityState != ModelState.Activity.LOADED
+                        && activityState != ModelState.Activity.PREEMPTED) {
                     throw new IllegalStateException("Model with handle: " + modelHandle
                             + " has invalid state for starting recognition");
                 }
 
                 // From here on, every exception isn't client's fault.
                 try {
-                    // Normally, we would set the state after the operation succeeds. However, since
-                    // the activity state may be reset outside of the lock, we set it here first,
-                    // and reset it in case of exception.
-                    modelState.setRecognitionConfig(config);
-                    modelState.setActivityState(ModelState.Activity.ACTIVE);
                     mDelegate.startRecognition(modelHandle, config);
+                    modelState.config = config;
+                    modelState.activityState = ModelState.Activity.ACTIVE;
                 } catch (Exception e) {
-                    modelState.setActivityState(ModelState.Activity.LOADED);
                     throw handleException(e);
                 }
             }
@@ -540,17 +484,36 @@
 
                 // From here on, every exception isn't client's fault.
                 try {
-                    // If the activity state is LOADED or INTERCEPTED, we skip delegating the
-                    // command, but still consider the call valid. In either case, the resulting
-                    // state is LOADED.
-                    if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
-                        mDelegate.stopRecognition(modelHandle);
+                    // If the activity state is INTERCEPTED, we skip delegating the command, but
+                    // still consider the call valid.
+                    if (modelState.activityState == ModelState.Activity.INTERCEPTED) {
+                        modelState.activityState = ModelState.Activity.LOADED;
+                        return;
                     }
-                    modelState.setActivityState(ModelState.Activity.LOADED);
                 } catch (Exception e) {
                     throw handleException(e);
                 }
             }
+
+            // Calling the delegate's stop must be done without the lock.
+            try {
+                mDelegate.stopRecognition(modelHandle);
+            } catch (Exception e) {
+                throw handleException(e);
+            }
+
+            synchronized (SoundTriggerMiddlewareValidation.this) {
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    // The model was unloaded while we let go of the lock.
+                    return;
+                }
+
+                // After the call, the state is LOADED, unless it has been first preempted.
+                if (modelState.activityState != ModelState.Activity.PREEMPTED) {
+                    modelState.activityState = ModelState.Activity.LOADED;
+                }
+            }
         }
 
         private void restartIfIntercepted(int modelHandle) {
@@ -561,12 +524,12 @@
                 }
                 ModelState modelState = mLoadedModels.get(modelHandle);
                 if (modelState == null
-                        || modelState.getActivityState() != ModelState.Activity.INTERCEPTED) {
+                        || modelState.activityState != ModelState.Activity.INTERCEPTED) {
                     return;
                 }
                 try {
-                    mDelegate.startRecognition(modelHandle, modelState.getRecognitionConfig());
-                    modelState.setActivityState(ModelState.Activity.ACTIVE);
+                    mDelegate.startRecognition(modelHandle, modelState.config);
+                    modelState.activityState = ModelState.Activity.ACTIVE;
                     Log.i(TAG, "Restarted intercepted model " + modelHandle);
                 } catch (Exception e) {
                     Log.i(TAG, "Failed to restart intercepted model " + modelHandle, e);
@@ -594,7 +557,7 @@
                 try {
                     // If the activity state is LOADED or INTERCEPTED, we skip delegating the
                     // command, but still consider the call valid.
-                    if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+                    if (modelState.activityState == ModelState.Activity.ACTIVE) {
                         mDelegate.forceRecognitionEvent(modelHandle);
                     }
                 } catch (Exception e) {
@@ -676,7 +639,7 @@
                 try {
                     ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
                             modelParam);
-                    modelState.updateParameterSupport(modelParam, result);
+                    modelState.parameterSupport.put(modelParam, result);
                     return result;
                 } catch (Exception e) {
                     throw handleException(e);
@@ -734,7 +697,7 @@
                 for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
                     pw.print(entry.getKey());
                     pw.print('\t');
-                    pw.print(entry.getValue().getActivityState().name());
+                    pw.print(entry.getValue().activityState.name());
                     pw.print('\t');
                     pw.print(entry.getValue().description);
                     pw.println();
@@ -764,78 +727,89 @@
             }
 
             @Override
-        public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
-            // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
-            // might be coming from the audio server (via setCaptureState()) while it is holding
-            // a lock that is also acquired while loading / unloading models. Thus, we require a
-            // strict locking order here, where obtaining our lock must always come first.
-            // To avoid this problem, we use an atomic model activity state. There is a risk of the
-            // model not being in the mLoadedModels map here, since it might have been stopped /
-            // unloaded while the event was in flight.
-            ModelState modelState = mLoadedModels.get(modelHandle);
-            if (modelState != null) {
-                if (event.status != RecognitionStatus.FORCED) {
-                    modelState.setActivityState(ModelState.Activity.LOADED);
-                }
-                try {
-                    mCallback.onRecognition(modelHandle, event);
-                } catch (Exception e) {
-                    // Dead client will be handled by binderDied() - no need to handle here.
-                    // In any case, client callbacks are considered best effort.
-                    Log.e(TAG, "Client callback exception.", e);
+            public void onRecognition(int modelHandle, @NonNull RecognitionEvent event,
+                    int captureSession) {
+                synchronized (SoundTriggerMiddlewareValidation.this) {
+                    ModelState modelState = mLoadedModels.get(modelHandle);
                     if (event.status != RecognitionStatus.FORCED) {
-                        modelState.setActivityState(ModelState.Activity.INTERCEPTED);
-                        // If we failed to deliver an actual event to the client, they would never
-                        // know to restart it whenever circumstances change. Thus, we restart it
-                        // here. We do this from a separate thread to avoid any race conditions.
-                        new Thread(() -> restartIfIntercepted(modelHandle)).start();
+                        modelState.activityState = ModelState.Activity.LOADED;
+                    }
+                }
+
+                // Calling the delegate callback must be done outside the lock.
+                try {
+                    mCallback.onRecognition(modelHandle, event, captureSession);
+                } catch (Exception e) {
+                    Log.w(TAG, "Client callback exception.", e);
+                    synchronized (SoundTriggerMiddlewareValidation.this) {
+                        ModelState modelState = mLoadedModels.get(modelHandle);
+                        if (event.status != RecognitionStatus.FORCED) {
+                            modelState.activityState = ModelState.Activity.INTERCEPTED;
+                            // If we failed to deliver an actual event to the client, they would
+                            // never know to restart it whenever circumstances change. Thus, we
+                            // restart it here. We do this from a separate thread to avoid any
+                            // race conditions.
+                            new Thread(() -> restartIfIntercepted(modelHandle)).start();
+                        }
                     }
                 }
             }
-        }
 
-        @Override
-        public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
-            // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
-            // might be coming from the audio server (via setCaptureState()) while it is holding
-            // a lock that is also acquired while loading / unloading models. Thus, we require a
-            // strict locking order here, where obtaining our lock must always come first.
-            // To avoid this problem, we use an atomic model activity state. There is a risk of the
-            // model not being in the mLoadedModels map here, since it might have been stopped /
-            // unloaded while the event was in flight.
-            ModelState modelState = mLoadedModels.get(modelHandle);
-            if (modelState != null) {
-                if (event.common.status != RecognitionStatus.FORCED) {
-                        modelState.setActivityState(ModelState.Activity.LOADED);
+            @Override
+            public void onPhraseRecognition(int modelHandle,
+                    @NonNull PhraseRecognitionEvent event, int captureSession) {
+                synchronized (SoundTriggerMiddlewareValidation.this) {
+                    ModelState modelState = mLoadedModels.get(modelHandle);
+                    if (event.common.status != RecognitionStatus.FORCED) {
+                        modelState.activityState = ModelState.Activity.LOADED;
+                    }
                 }
+
+                // Calling the delegate callback must be done outside the lock.
                 try {
-                    mCallback.onPhraseRecognition(modelHandle, event);
+                    mCallback.onPhraseRecognition(modelHandle, event, captureSession);
                 } catch (Exception e) {
+                    Log.w(TAG, "Client callback exception.", e);
+                    synchronized (SoundTriggerMiddlewareValidation.this) {
+                        ModelState modelState = mLoadedModels.get(modelHandle);
+                        if (event.common.status != RecognitionStatus.FORCED) {
+                            modelState.activityState = ModelState.Activity.INTERCEPTED;
+                            // If we failed to deliver an actual event to the client, they would
+                            // never know to restart it whenever circumstances change. Thus, we
+                            // restart it here. We do this from a separate thread to avoid any
+                            // race conditions.
+                            new Thread(() -> restartIfIntercepted(modelHandle)).start();
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public void onModelUnloaded(int modelHandle) {
+                synchronized (SoundTriggerMiddlewareValidation.this) {
+                    ModelState modelState = mLoadedModels.get(modelHandle);
+                    modelState.activityState = ModelState.Activity.PREEMPTED;
+                }
+
+                // Calling the delegate callback must be done outside the lock.
+                try {
+                    mCallback.onModelUnloaded(modelHandle);
+                } catch (Exception e) {
+                    Log.w(TAG, "Client callback exception.", e);
+                }
+            }
+
+            @Override
+            public void onResourcesAvailable() {
+                // Not locking to avoid deadlocks (not affecting any state).
+                try {
+                    mCallback.onResourcesAvailable();
+                } catch (RemoteException e) {
                     // Dead client will be handled by binderDied() - no need to handle here.
                     // In any case, client callbacks are considered best effort.
                     Log.e(TAG, "Client callback exception.", e);
-                    if (event.common.status != RecognitionStatus.FORCED) {
-                        modelState.setActivityState(ModelState.Activity.INTERCEPTED);
-                        // If we failed to deliver an actual event to the client, they would never
-                        // know to restart it whenever circumstances change. Thus, we restart it
-                        // here. We do this from a separate thread to avoid any race conditions.
-                        new Thread(() -> restartIfIntercepted(modelHandle)).start();
-                    }
                 }
             }
-        }
-
-        @Override
-        public void onRecognitionAvailabilityChange(boolean available) {
-            // Not locking to avoid deadlocks (not affecting any state).
-            try {
-                mCallback.onRecognitionAvailabilityChange(available);
-            } catch (RemoteException e) {
-                // Dead client will be handled by binderDied() - no need to handle here.
-                // In any case, client callbacks are considered best effort.
-                Log.e(TAG, "Client callback exception.", e);
-            }
-        }
 
             @Override
             public void onModuleDied() {
@@ -860,8 +834,7 @@
                         // Gracefully stop all active recognitions and unload the models.
                         for (Map.Entry<Integer, ModelState> entry :
                                 mLoadedModels.entrySet()) {
-                            if (entry.getValue().getActivityState()
-                                    == ModelState.Activity.ACTIVE) {
+                            if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
                                 mDelegate.stopRecognition(entry.getKey());
                             }
                             mDelegate.unloadModel(entry.getKey());
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 02d978d..f211158 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -18,30 +18,24 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
-import android.os.IHwBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -71,9 +65,8 @@
  * <li>There is no binder instance associated with this implementation. Do not call asBinder().
  * <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
  * recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
- * thrown should be regarded as a bug in the implementation or one of its dependencies
- * (assuming correct usage).
+ * {@link android.media.soundtrigger.Status} constants. Any other exception thrown should be
+ * regarded as a bug in the implementation or one of its dependencies (assuming correct usage).
  * <li>The implementation is designed for testability by featuring dependency injection (the
  * underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
  * on Android runtime.
@@ -88,15 +81,13 @@
  *
  * @hide
  */
-class SoundTriggerModule implements IHwBinder.DeathRecipient {
+class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.GlobalCallback {
     static private final String TAG = "SoundTriggerModule";
-    @NonNull private HalFactory mHalFactory;
-    @NonNull private ISoundTriggerHw2 mHalService;
+    @NonNull private final HalFactory mHalFactory;
+    @NonNull private ISoundTriggerHal mHalService;
     @NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
     private final Set<Session> mActiveSessions = new HashSet<>();
-    private int mNumLoadedModels = 0;
-    private SoundTriggerModuleProperties mProperties;
-    private boolean mRecognitionAvailable;
+    private Properties mProperties;
 
     /**
      * Ctor.
@@ -110,9 +101,6 @@
         mAudioSessionProvider = audioSessionProvider;
 
         attachToHal();
-        mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
-        // We conservatively assume that external capture is active until explicitly told otherwise.
-        mRecognitionAvailable = mProperties.concurrentCapture;
     }
 
     /**
@@ -140,50 +128,13 @@
      * @return The properties structure.
      */
     synchronized @NonNull
-    SoundTriggerModuleProperties getProperties() {
+    Properties getProperties() {
         return mProperties;
     }
 
-    /**
-     * Notify the module that external capture has started / finished, using the same input device
-     * used for recognition.
-     * If the underlying driver does not support recognition while capturing, capture will be
-     * aborted, and the recognition callback will receive and abort event. In addition, all active
-     * clients will be notified of the change in state.
-     *
-     * @param active true iff external capture is active.
-     */
-    void setExternalCaptureState(boolean active) {
-        // We should never invoke callbacks while holding the lock, since this may deadlock with
-        // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
-        // the lock, but invoke them after releasing it.
-        List<Runnable> callbacks = new LinkedList<>();
-
-        synchronized (this) {
-            if (mProperties.concurrentCapture) {
-                // If we support concurrent capture, we don't care about any of this.
-                return;
-            }
-            mRecognitionAvailable = !active;
-            if (!mRecognitionAvailable) {
-                // Our module does not support recognition while a capture is active -
-                // need to abort all active recognitions.
-                for (Session session : mActiveSessions) {
-                    session.abortActiveRecognitions(callbacks);
-                }
-            }
-        }
-        for (Runnable callback : callbacks) {
-            callback.run();
-        }
-        for (Session session : mActiveSessions) {
-            session.notifyRecognitionAvailability();
-        }
-    }
-
     @Override
-    public void serviceDied(long cookie) {
-        Log.w(TAG, String.format("Underlying HAL driver died."));
+    public void binderDied() {
+        Log.w(TAG, "Underlying HAL driver died.");
         List<ISoundTriggerCallback> callbacks;
         synchronized (this) {
             callbacks = new ArrayList<>(mActiveSessions.size());
@@ -207,20 +158,19 @@
      * Resets the transient state of this object.
      */
     private void reset() {
+        mHalService.detach();
         attachToHal();
-        // We conservatively assume that external capture is active until explicitly told otherwise.
-        mRecognitionAvailable = mProperties.concurrentCapture;
-        mNumLoadedModels = 0;
     }
 
     /**
      * Attached to the HAL service via factory.
      */
     private void attachToHal() {
-        mHalService = new SoundTriggerHw2Enforcer(
-                new SoundTriggerHw2Watchdog(
-                        new SoundTriggerHw2Compat(mHalFactory.create())));
-        mHalService.linkToDeath(this, 0);
+        mHalService = new SoundTriggerHalEnforcer(
+                new SoundTriggerHalWatchdog(mHalFactory.create()));
+        mHalService.linkToDeath(this);
+        mHalService.registerCallback(this);
+        mProperties = mHalService.getProperties();
     }
 
     /**
@@ -232,6 +182,25 @@
         mActiveSessions.remove(session);
     }
 
+    @Override
+    public void onResourcesAvailable() {
+        List<ISoundTriggerCallback> callbacks;
+        synchronized (this) {
+            callbacks = new ArrayList<>(mActiveSessions.size());
+            for (Session session : mActiveSessions) {
+                callbacks.add(session.mCallback);
+            }
+        }
+        // Trigger the callbacks outside of the lock to avoid deadlocks.
+        for (ISoundTriggerCallback callback : callbacks) {
+            try {
+                callback.onResourcesAvailable();
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+    }
+
     /** State of a single sound model. */
     private enum ModelState {
         /** Initial state, until load() is called. */
@@ -249,7 +218,7 @@
      */
     private class Session implements ISoundTriggerModule {
         private ISoundTriggerCallback mCallback;
-        private Map<Integer, Model> mLoadedModels = new HashMap<>();
+        private final Map<Integer, Model> mLoadedModels = new HashMap<>();
 
         /**
          * Ctor.
@@ -258,7 +227,6 @@
          */
         private Session(@NonNull ISoundTriggerCallback callback) {
             mCallback = callback;
-            notifyRecognitionAvailability();
         }
 
         @Override
@@ -274,95 +242,65 @@
 
         @Override
         public int loadModel(@NonNull SoundModel model) {
-            // We must do this outside the lock, to avoid possible deadlocks with the remote process
-            // that provides the audio sessions, which may also be calling into us.
-            SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
-                    mAudioSessionProvider.acquireSession();
-
-            try {
-                synchronized (SoundTriggerModule.this) {
-                    checkValid();
-                    if (mNumLoadedModels == mProperties.maxSoundModels) {
-                        throw new RecoverableException(Status.RESOURCE_CONTENTION,
-                                "Maximum number of models loaded.");
-                    }
-                    Model loadedModel = new Model();
-                    int result = loadedModel.load(model, audioSession);
-                    ++mNumLoadedModels;
-                    return result;
-                }
-            } catch (Exception e) {
-                // We must do this outside the lock, to avoid possible deadlocks with the remote
-                // process that provides the audio sessions, which may also be calling into us.
+            synchronized (SoundTriggerModule.this) {
+                SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+                        mAudioSessionProvider.acquireSession();
                 try {
-                    mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
-                } catch (Exception ee) {
-                    Log.e(TAG, "Failed to release session.", ee);
+                    checkValid();
+                    Model loadedModel = new Model();
+                    return loadedModel.load(model, audioSession);
+                } catch (Exception e) {
+                    // We must do this outside the lock, to avoid possible deadlocks with the remote
+                    // process that provides the audio sessions, which may also be calling into us.
+                    try {
+                        mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                    } catch (Exception ee) {
+                        Log.e(TAG, "Failed to release session.", ee);
+                    }
+                    throw e;
                 }
-                throw e;
             }
         }
 
         @Override
         public int loadPhraseModel(@NonNull PhraseSoundModel model) {
-            // We must do this outside the lock, to avoid possible deadlocks with the remote process
-            // that provides the audio sessions, which may also be calling into us.
-            SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
-                    mAudioSessionProvider.acquireSession();
-
-            try {
-                synchronized (SoundTriggerModule.this) {
+            synchronized (SoundTriggerModule.this) {
+                SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+                        mAudioSessionProvider.acquireSession();
+                try {
                     checkValid();
-                    if (mNumLoadedModels == mProperties.maxSoundModels) {
-                        throw new RecoverableException(Status.RESOURCE_CONTENTION,
-                                "Maximum number of models loaded.");
-                    }
                     Model loadedModel = new Model();
                     int result = loadedModel.load(model, audioSession);
-                    ++mNumLoadedModels;
                     Log.d(TAG, String.format("loadPhraseModel()->%d", result));
                     return result;
+                } catch (Exception e) {
+                    // We must do this outside the lock, to avoid possible deadlocks with the remote
+                    // process that provides the audio sessions, which may also be calling into us.
+                    try {
+                        mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                    } catch (Exception ee) {
+                        Log.e(TAG, "Failed to release session.", ee);
+                    }
+                    throw e;
                 }
-            } catch (Exception e) {
-                // We must do this outside the lock, to avoid possible deadlocks with the remote
-                // process that provides the audio sessions, which may also be calling into us.
-                try {
-                    mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
-                } catch (Exception ee) {
-                    Log.e(TAG, "Failed to release session.", ee);
-                }
-                throw e;
             }
         }
 
         @Override
         public void unloadModel(int modelHandle) {
-            int sessionId;
             synchronized (SoundTriggerModule.this) {
+                int sessionId;
                 checkValid();
                 sessionId = mLoadedModels.get(modelHandle).unload();
-                --mNumLoadedModels;
+                mAudioSessionProvider.releaseSession(sessionId);
             }
-
-            // We must do this outside the lock, to avoid possible deadlocks with the remote process
-            // that provides the audio sessions, which may also be calling into us.
-            mAudioSessionProvider.releaseSession(sessionId);
         }
 
         @Override
         public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
-            // We should never invoke callbacks while holding the lock, since this may deadlock with
-            // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
-            // the lock, but invoke them after releasing it.
-            List<Runnable> callbacks = new LinkedList<>();
-
             synchronized (SoundTriggerModule.this) {
                 checkValid();
-                mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
-            }
-
-            for (Runnable callback : callbacks) {
-                callback.run();
+                mLoadedModels.get(modelHandle).startRecognition(config);
             }
         }
 
@@ -407,27 +345,6 @@
         }
 
         /**
-         * Abort all currently active recognitions.
-         * @param callbacks Will be appended with a list of callbacks that need to be invoked
-         *                  after this method returns, without holding the module lock.
-         */
-        private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
-            for (Model model : mLoadedModels.values()) {
-                model.abortActiveRecognition(callbacks);
-            }
-        }
-
-        private void notifyRecognitionAvailability() {
-            try {
-                mCallback.onRecognitionAvailabilityChange(mRecognitionAvailable);
-            } catch (RemoteException e) {
-                // Dead client will be handled by binderDied() - no need to handle here.
-                // In any case, client callbacks are considered best effort.
-                Log.e(TAG, "Client callback execption.", e);
-            }
-        }
-
-        /**
          * The underlying module HAL is dead.
          * @return The client callback that needs to be invoked to notify the client.
          */
@@ -455,10 +372,9 @@
          *
          * All model-based operations are delegated to this class and implemented here.
          */
-        private class Model implements ISoundTriggerHw2.Callback {
+        private class Model implements ISoundTriggerHal.ModelCallback {
             public int mHandle;
             private ModelState mState = ModelState.INIT;
-            private int mModelType = SoundModelType.UNKNOWN;
             private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession;
 
             private @NonNull
@@ -473,11 +389,8 @@
 
             private int load(@NonNull SoundModel model,
                     SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
-                mModelType = model.type;
                 mSession = audioSession;
-                ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
-
-                mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
+                mHandle = mHalService.loadSoundModel(model, this);
                 setState(ModelState.LOADED);
                 mLoadedModels.put(mHandle, this);
                 return mHandle;
@@ -485,12 +398,8 @@
 
             private int load(@NonNull PhraseSoundModel model,
                     SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
-                mModelType = model.common.type;
                 mSession = audioSession;
-                ISoundTriggerHw.PhraseSoundModel hidlModel =
-                        ConversionUtil.aidl2hidlPhraseSoundModel(model);
-
-                mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
+                mHandle = mHalService.loadPhraseSoundModel(model, this);
 
                 setState(ModelState.LOADED);
                 mLoadedModels.put(mHandle, this);
@@ -507,18 +416,9 @@
                 return mSession.mSessionHandle;
             }
 
-            private void startRecognition(@NonNull RecognitionConfig config,
-                    @NonNull List<Runnable> callbacks) {
-                if (!mRecognitionAvailable) {
-                    // Recognition is unavailable - send an abort event immediately.
-                    callbacks.add(this::notifyAbort);
-                    return;
-                }
-                android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
-                        ConversionUtil.aidl2hidlRecognitionConfig(config);
-                hidlConfig.base.header.captureDevice = mSession.mDeviceHandle;
-                hidlConfig.base.header.captureHandle = mSession.mIoHandle;
-                mHalService.startRecognition(mHandle, hidlConfig, this, 0);
+            private void startRecognition(@NonNull RecognitionConfig config) {
+                mHalService.startRecognition(mHandle, mSession.mDeviceHandle,
+                        mSession.mIoHandle, config);
                 setState(ModelState.ACTIVE);
             }
 
@@ -537,7 +437,7 @@
                     // This call is idempotent in order to avoid races.
                     return;
                 }
-                mHalService.getModelState(mHandle);
+                mHalService.forceRecognitionEvent(mHandle);
             }
 
 
@@ -553,78 +453,24 @@
 
             @Nullable
             private ModelParameterRange queryModelParameterSupport(int modelParam) {
-                return ConversionUtil.hidl2aidlModelParameterRange(
-                        mHalService.queryParameter(mHandle,
-                                ConversionUtil.aidl2hidlModelParameter(modelParam)));
-            }
-
-            /**
-             * Abort the recognition, if active.
-             * @param callbacks Will be appended with a list of callbacks that need to be invoked
-             *                  after this method returns, without holding the module lock.
-             */
-            private void abortActiveRecognition(List<Runnable> callbacks) {
-                // If we're inactive, do nothing.
-                if (getState() != ModelState.ACTIVE) {
-                    return;
-                }
-                // Stop recognition.
-                stopRecognition();
-
-                // Notify the client that recognition has been aborted.
-                callbacks.add(this::notifyAbort);
-            }
-
-            /** Notify the client that recognition has been aborted. */
-            private void notifyAbort() {
-                try {
-                    switch (mModelType) {
-                        case SoundModelType.GENERIC: {
-                            android.media.soundtrigger_middleware.RecognitionEvent event =
-                                    newEmptyRecognitionEvent();
-                            event.status =
-                                    android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
-                            event.type = SoundModelType.GENERIC;
-                            mCallback.onRecognition(mHandle, event);
-                        }
-                        break;
-
-                        case SoundModelType.KEYPHRASE: {
-                            android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
-                                    newEmptyPhraseRecognitionEvent();
-                            event.common.status =
-                                    android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
-                            event.common.type = SoundModelType.KEYPHRASE;
-                            mCallback.onPhraseRecognition(mHandle, event);
-                        }
-                        break;
-
-                        default:
-                            Log.e(TAG, "Unknown model type: " + mModelType);
-
-                    }
-                } catch (RemoteException e) {
-                    // Dead client will be handled by binderDied() - no need to handle here.
-                    // In any case, client callbacks are considered best effort.
-                    Log.e(TAG, "Client callback execption.", e);
-                }
+                return mHalService.queryParameter(mHandle, modelParam);
             }
 
             @Override
-            public void recognitionCallback(
-                    @NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
-                    int cookie) {
-                RecognitionEvent aidlEvent =
-                        ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
-                aidlEvent.captureSession = mSession.mSessionHandle;
+            public void recognitionCallback(int modelHandle,
+                    @NonNull RecognitionEvent recognitionEvent) {
+                ISoundTriggerCallback callback;
                 synchronized (SoundTriggerModule.this) {
-                    if (aidlEvent.status != RecognitionStatus.FORCED) {
+                    if (recognitionEvent.status != RecognitionStatus.FORCED) {
                         setState(ModelState.LOADED);
                     }
+                    callback = mCallback;
                 }
                 // The callback must be invoked outside of the lock.
                 try {
-                    mCallback.onRecognition(mHandle, aidlEvent);
+                    if (callback != null) {
+                        callback.onRecognition(mHandle, recognitionEvent, mSession.mSessionHandle);
+                    }
                 } catch (RemoteException e) {
                     // We're not expecting any exceptions here.
                     throw e.rethrowAsRuntimeException();
@@ -632,22 +478,40 @@
             }
 
             @Override
-            public void phraseRecognitionCallback(
-                    @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
-                    int cookie) {
-                PhraseRecognitionEvent aidlEvent =
-                        ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
-                aidlEvent.common.captureSession = mSession.mSessionHandle;
-
+            public void phraseRecognitionCallback(int modelHandle,
+                    @NonNull PhraseRecognitionEvent phraseRecognitionEvent) {
+                ISoundTriggerCallback callback;
                 synchronized (SoundTriggerModule.this) {
-                    if (aidlEvent.common.status != RecognitionStatus.FORCED) {
+                    if (phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
                         setState(ModelState.LOADED);
                     }
+                    callback = mCallback;
                 }
 
                 // The callback must be invoked outside of the lock.
                 try {
-                    mCallback.onPhraseRecognition(mHandle, aidlEvent);
+                    if (callback != null) {
+                        mCallback.onPhraseRecognition(mHandle, phraseRecognitionEvent,
+                                mSession.mSessionHandle);
+                    }
+                } catch (RemoteException e) {
+                    // We're not expecting any exceptions here.
+                    throw e.rethrowAsRuntimeException();
+                }
+            }
+
+            @Override
+            public void modelUnloaded(int modelHandle) {
+                ISoundTriggerCallback callback;
+                synchronized (SoundTriggerModule.this) {
+                    callback = mCallback;
+                }
+
+                // The callback must be invoked outside of the lock.
+                try {
+                    if (callback != null) {
+                        callback.onModelUnloaded(modelHandle);
+                    }
                 } catch (RemoteException e) {
                     // We're not expecting any exceptions here.
                     throw e.rethrowAsRuntimeException();
@@ -655,33 +519,4 @@
             }
         }
     }
-
-    /**
-     * Creates a default-initialized recognition event.
-     *
-     * Non-nullable object fields are default constructed.
-     * Non-nullable array fields are initialized to 0 length.
-     *
-     * @return The event.
-     */
-    private static RecognitionEvent newEmptyRecognitionEvent() {
-        RecognitionEvent result = new RecognitionEvent();
-        result.data = new byte[0];
-        return result;
-    }
-
-    /**
-     * Creates a default-initialized phrase recognition event.
-     *
-     * Non-nullable object fields are default constructed.
-     * Non-nullable array fields are initialized to 0 length.
-     *
-     * @return The event.
-     */
-    private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() {
-        PhraseRecognitionEvent result = new PhraseRecognitionEvent();
-        result.common = newEmptyRecognitionEvent();
-        result.phraseExtras = new PhraseRecognitionExtra[0];
-        return result;
-    }
 }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index e05c468..4d52121 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -17,21 +17,18 @@
 package com.android.server.soundtrigger_middleware;
 
 import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-
-import com.android.internal.util.Preconditions;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
 
 import java.util.Objects;
 import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Utilities for asserting the validity of various data types used by this module.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a436e6b..d95e826 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -132,7 +133,7 @@
     /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
     void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, boolean isFullscreen);
+            @Behavior int behavior, InsetsState requestedState, String packageName);
 
     /** @see com.android.internal.statusbar.IStatusBar#showTransient */
     void showTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3a7e13b..47fdc4e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -59,6 +59,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -526,13 +527,13 @@
         @Override
         public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
                 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-                @Behavior int behavior, boolean isFullscreen) {
+                @Behavior int behavior, InsetsState requestedState, String packageName) {
             getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
-                    navbarColorManagedByIme, behavior, isFullscreen);
+                    navbarColorManagedByIme, behavior, requestedState, packageName);
             if (mBar != null) {
                 try {
                     mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
-                            navbarColorManagedByIme, behavior, isFullscreen);
+                            navbarColorManagedByIme, behavior, requestedState, packageName);
                 } catch (RemoteException ex) { }
             }
         }
@@ -1103,13 +1104,14 @@
         return state;
     }
 
-    private class UiState {
+    private static class UiState {
         private @Appearance int mAppearance = 0;
         private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
-        private ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
+        private final ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
         private boolean mNavbarColorManagedByIme = false;
         private @Behavior int mBehavior;
-        private boolean mFullscreen = false;
+        private InsetsState mRequestedState = new InsetsState();
+        private String mPackageName = "none";
         private int mDisabled1 = 0;
         private int mDisabled2 = 0;
         private int mImeWindowVis = 0;
@@ -1119,12 +1121,13 @@
 
         private void setBarAttributes(@Appearance int appearance,
                 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-                @Behavior int behavior, boolean isFullscreen) {
+                @Behavior int behavior, InsetsState requestedState, String packageName) {
             mAppearance = appearance;
             mAppearanceRegions = appearanceRegions;
             mNavbarColorManagedByIme = navbarColorManagedByIme;
             mBehavior = behavior;
-            mFullscreen = isFullscreen;
+            mRequestedState = requestedState;
+            mPackageName = packageName;
         }
 
         private void showTransient(@InternalInsetsType int[] types) {
@@ -1244,8 +1247,8 @@
                     state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
                     state.mImeBackDisposition, state.mShowImeSwitcher,
                     gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
-                    state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen,
-                    transientBarTypes);
+                    state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedState,
+                    state.mPackageName, transientBarTypes);
         }
     }
 
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 8d79a81..0b34eb8 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -368,16 +368,12 @@
         mExternalStorageServicePackageName = provider.applicationInfo.packageName;
         mExternalStorageServiceAppId = UserHandle.getAppId(provider.applicationInfo.uid);
 
-        Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
-        intent.setPackage(mExternalStorageServicePackageName);
-        ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
-        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+        ServiceInfo serviceInfo = resolveExternalStorageServiceAsUser(UserHandle.USER_SYSTEM);
+        if (serviceInfo == null) {
             throw new ExternalStorageServiceException(
                     "No valid ExternalStorageService component found");
         }
 
-        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
         ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
         if (!Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
                 .equals(serviceInfo.permission)) {
@@ -490,4 +486,24 @@
     private boolean shouldHandle(@Nullable VolumeInfo vol) {
         return !mIsResetting && (vol == null || isSupportedVolume(vol));
     }
+
+    /**
+     * Returns {@code true} if the given user supports external storage,
+     * {@code false} otherwise.
+     */
+    public boolean supportsExternalStorage(int userId) {
+        return resolveExternalStorageServiceAsUser(userId) != null;
+    }
+
+    private ServiceInfo resolveExternalStorageServiceAsUser(int userId) {
+        Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
+        intent.setPackage(mExternalStorageServicePackageName);
+        ResolveInfo resolveInfo = mContext.getPackageManager().resolveServiceAsUser(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId);
+        if (resolveInfo == null) {
+            return null;
+        }
+
+        return resolveInfo.serviceInfo;
+    }
 }
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
new file mode 100644
index 0000000..f1bfea7
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timedetector."
+        }
+      ]
+    },
+    {
+      "name": "CtsTimeTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 357c232..e751a7b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -42,6 +42,7 @@
 import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ReferenceWithHistory;
 
+import java.time.Duration;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Objects;
@@ -321,12 +322,16 @@
         ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
         ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
                 + mEnvironment.isAutoTimeDetectionEnabled());
-        ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
-        ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+        long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+        ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n",
+                Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis);
+        long systemClockMillis = mEnvironment.systemClockMillis();
+        ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n",
+                Instant.ofEpochMilli(systemClockMillis), systemClockMillis);
         ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
                 + mEnvironment.systemClockUpdateThresholdMillis());
         Instant autoTimeLowerBound = mEnvironment.autoTimeLowerBound();
-        ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+        ipw.printf("mEnvironment.autoTimeLowerBound()=%s (%s)\n",
                 autoTimeLowerBound, autoTimeLowerBound.toEpochMilli());
         String priorities =
                 Arrays.stream(mEnvironment.autoOriginPriorities())
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 4a1d9c4..23f6bbe 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -86,11 +86,9 @@
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
             }));
 
-    // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
-    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
-    // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented
+    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
     private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
-            Duration.ofSeconds(20);
+            Duration.ofMinutes(1);
     private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
 
     private static final Object SLOCK = new Object();
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 91e172c..b1ae626 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -7,6 +7,14 @@
           "include-filter": "com.android.server.timezonedetector."
         }
       ]
+    },
+    {
+      "name": "CtsTimeTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ]
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index f054c57..59df6ff 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -67,7 +67,7 @@
 
             @Override
             public void onProviderUnbound() {
-                handleProviderLost("onProviderUnbound()");
+                handleTemporaryFailure("onProviderUnbound()");
             }
         });
     }
@@ -77,50 +77,6 @@
         mProxy.destroy();
     }
 
-    private void handleProviderLost(String reason) {
-        mThreadingDomain.assertCurrentThread();
-
-        synchronized (mSharedLock) {
-            ProviderState currentState = mCurrentState.get();
-            switch (currentState.stateEnum) {
-                case PROVIDER_STATE_STARTED_INITIALIZING:
-                case PROVIDER_STATE_STARTED_UNCERTAIN:
-                case PROVIDER_STATE_STARTED_CERTAIN: {
-                    // Losing a remote provider is treated as becoming uncertain.
-                    String msg = "handleProviderLost reason=" + reason
-                            + ", mProviderName=" + mProviderName
-                            + ", currentState=" + currentState;
-                    debugLog(msg);
-                    // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
-                    // event == null
-                    ProviderState newState = currentState.newState(
-                            PROVIDER_STATE_STARTED_UNCERTAIN, null,
-                            currentState.currentUserConfiguration, msg);
-                    setCurrentState(newState, true);
-                    break;
-                }
-                case PROVIDER_STATE_STOPPED: {
-                    debugLog("handleProviderLost reason=" + reason
-                            + ", mProviderName=" + mProviderName
-                            + ", currentState=" + currentState
-                            + ": No state change required, provider is stopped.");
-                    break;
-                }
-                case PROVIDER_STATE_PERM_FAILED:
-                case PROVIDER_STATE_DESTROYED: {
-                    debugLog("handleProviderLost reason=" + reason
-                            + ", mProviderName=" + mProviderName
-                            + ", currentState=" + currentState
-                            + ": No state change required, provider is terminated.");
-                    break;
-                }
-                default: {
-                    throw new IllegalStateException("Unknown currentState=" + currentState);
-                }
-            }
-        }
-    }
-
     private void handleOnProviderBound() {
         mThreadingDomain.assertCurrentThread();
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index d2190fd..b1019f3 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -166,12 +166,6 @@
             stopProviders();
             mPrimaryProvider.destroy();
             mSecondaryProvider.destroy();
-
-            // If the controller has made a "certain" suggestion, it should make an uncertain
-            // suggestion to cancel it.
-            if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
-                makeSuggestion(createUncertainSuggestion("Controller is destroyed"));
-            }
         }
     }
 
@@ -182,6 +176,16 @@
 
         // By definition, if both providers are stopped, the controller is uncertain.
         cancelUncertaintyTimeout();
+
+        // If a previous "certain" suggestion has been made, then a new "uncertain"
+        // suggestion must now be made to indicate the controller {does not / no longer has}
+        // an opinion and will not be sending further updates (until at least the providers are
+        // re-started).
+        if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+            GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                    "Providers are stopping");
+            makeSuggestion(suggestion);
+        }
     }
 
     @GuardedBy("mSharedLock")
@@ -275,21 +279,6 @@
             }
         } else {
             stopProviders();
-
-            // There can be an uncertainty timeout set if the controller most recently received
-            // an uncertain event. This is a no-op if there isn't a timeout set.
-            cancelUncertaintyTimeout();
-
-            // If a previous "certain" suggestion has been made, then a new "uncertain"
-            // suggestion must now be made to indicate the controller {does not / no longer has}
-            // an opinion and will not be sending further updates (until at least the config
-            // changes again and providers are re-started).
-            if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
-                GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
-                        "Provider is stopped:"
-                                + " primary=" + mPrimaryProvider.getCurrentState());
-                makeSuggestion(suggestion);
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index e116a87..c2add2d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -592,9 +592,7 @@
                     PROVIDER_STATE_STOPPED, null, null, "stopUpdates() called");
             setCurrentState(newState, false);
 
-            if (mInitializationTimeoutQueue.hasQueued()) {
-                mInitializationTimeoutQueue.cancel();
-            }
+            cancelInitializationTimeoutIfSet();
 
             onStopUpdates();
         }
@@ -656,9 +654,7 @@
                             ProviderState newState = currentState.newState(
                                     PROVIDER_STATE_PERM_FAILED, null, null, msg);
                             setCurrentState(newState, true);
-                            if (mInitializationTimeoutQueue.hasQueued()) {
-                                mInitializationTimeoutQueue.cancel();
-                            }
+                            cancelInitializationTimeoutIfSet();
                             return;
                         }
                         case EVENT_TYPE_SUGGESTION:
@@ -691,9 +687,7 @@
                             ProviderState newState = currentState.newState(
                                     PROVIDER_STATE_PERM_FAILED, null, null, msg);
                             setCurrentState(newState, true);
-                            if (mInitializationTimeoutQueue.hasQueued()) {
-                                mInitializationTimeoutQueue.cancel();
-                            }
+                            cancelInitializationTimeoutIfSet();
 
                             return;
                         }
@@ -709,9 +703,7 @@
                                     timeZoneProviderEvent, currentState.currentUserConfiguration,
                                     "handleTimeZoneProviderEvent() when started");
                             setCurrentState(newState, true);
-                            if (mInitializationTimeoutQueue.hasQueued()) {
-                                mInitializationTimeoutQueue.cancel();
-                            }
+                            cancelInitializationTimeoutIfSet();
                             return;
                         }
                         default: {
@@ -727,6 +719,52 @@
         }
     }
 
+    /** For subclasses to invoke when needing to report a temporary failure. */
+    final void handleTemporaryFailure(String reason) {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            ProviderState currentState = mCurrentState.get();
+            switch (currentState.stateEnum) {
+                case PROVIDER_STATE_STARTED_INITIALIZING:
+                case PROVIDER_STATE_STARTED_UNCERTAIN:
+                case PROVIDER_STATE_STARTED_CERTAIN: {
+                    // A temporary failure is treated as becoming uncertain.
+                    String msg = "handleProviderLost reason=" + reason
+                            + ", mProviderName=" + mProviderName
+                            + ", currentState=" + currentState;
+                    debugLog(msg);
+                    // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
+                    // event == null
+                    ProviderState newState = currentState.newState(
+                            PROVIDER_STATE_STARTED_UNCERTAIN, null,
+                            currentState.currentUserConfiguration, msg);
+                    setCurrentState(newState, true);
+                    cancelInitializationTimeoutIfSet();
+                    break;
+                }
+                case PROVIDER_STATE_STOPPED: {
+                    debugLog("handleProviderLost reason=" + reason
+                            + ", mProviderName=" + mProviderName
+                            + ", currentState=" + currentState
+                            + ": No state change required, provider is stopped.");
+                    break;
+                }
+                case PROVIDER_STATE_PERM_FAILED:
+                case PROVIDER_STATE_DESTROYED: {
+                    debugLog("handleProviderLost reason=" + reason
+                            + ", mProviderName=" + mProviderName
+                            + ", currentState=" + currentState
+                            + ": No state change required, provider is terminated.");
+                    break;
+                }
+                default: {
+                    throw new IllegalStateException("Unknown currentState=" + currentState);
+                }
+            }
+        }
+    }
+
     @GuardedBy("mSharedLock")
     private void assertIsStarted() {
         ProviderState currentState = mCurrentState.get();
@@ -751,6 +789,13 @@
         }
     }
 
+    @GuardedBy("mSharedLock")
+    private void cancelInitializationTimeoutIfSet() {
+        if (mInitializationTimeoutQueue.hasQueued()) {
+            mInitializationTimeoutQueue.cancel();
+        }
+    }
+
     @VisibleForTesting
     Duration getInitializationTimeoutDelay() {
         synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 5723e1d..ee30fa2 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,8 +50,6 @@
      */
     private final int mProcessId;
 
-    private boolean mIsForeground;
-
     /**
      * All the clients that share the same resource would be under the same group id.
      *
@@ -90,6 +88,12 @@
     private int mUsingCiCamId = INVALID_RESOURCE_ID;
 
     /**
+     * If the priority is overwritten through
+     * {@link TunerResourceManagerService#setPriority(int, int)}.
+     */
+    private boolean mIsPriorityOverwritten = false;
+
+    /**
      * Optional arbitrary priority value given by the client.
      *
      * <p>This value can override the default priorotiy calculated from
@@ -121,17 +125,10 @@
     }
 
     /**
-     * Set the current isForeground status.
+     * If the client priority is overwrttien.
      */
-    public void setForeground(boolean isForeground) {
-        mIsForeground = isForeground;
-    }
-
-    /**
-     * Get the previous recorded isForeground status.
-     */
-    public boolean isForeground() {
-        return mIsForeground;
+    public boolean isPriorityOverwritten() {
+        return mIsPriorityOverwritten;
     }
 
     public int getGroupId() {
@@ -153,6 +150,17 @@
         mPriority = priority;
     }
 
+    /**
+     * Overwrite the client priority.
+     */
+    public void overwritePriority(int priority) {
+        if (priority < 0) {
+            return;
+        }
+        mIsPriorityOverwritten = true;
+        mPriority = priority;
+    }
+
     public void setNiceValue(int niceValue) {
         mNiceValue = niceValue;
     }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 988582d..0c04b07 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -507,9 +507,8 @@
                                               .useCase(profile.useCase)
                                               .processId(pid)
                                               .build();
-        clientProfile.setForeground(checkIsForeground(pid));
         clientProfile.setPriority(
-                getClientPriority(profile.useCase, clientProfile.isForeground()));
+                getClientPriority(profile.useCase, checkIsForeground(pid)));
 
         addClientProfile(clientId[0], clientProfile, listener);
     }
@@ -547,8 +546,7 @@
             return false;
         }
 
-        profile.setForeground(checkIsForeground(profile.getProcessId()));
-        profile.setPriority(priority);
+        profile.overwritePriority(priority);
         profile.setNiceValue(niceValue);
 
         return true;
@@ -694,7 +692,7 @@
                 } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
                     // Record the frontend id with the lowest client priority among all the
                     // in use frontends when no available frontend has been found.
-                    int priority = getOwnerClientPriority(fr.getOwnerClientId());
+                    int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
                     if (currentLowestPriority > priority) {
                         inUseLowestPriorityFrHandle = fr.getHandle();
                         currentLowestPriority = priority;
@@ -760,7 +758,7 @@
             } else {
                 // Record the lnb id with the lowest client priority among all the
                 // in use lnb when no available lnb has been found.
-                int priority = getOwnerClientPriority(lnb.getOwnerClientId());
+                int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
                 if (currentLowestPriority > priority) {
                     inUseLowestPriorityLnbHandle = lnb.getHandle();
                     currentLowestPriority = priority;
@@ -818,7 +816,7 @@
         }
         for (int ownerId : cas.getOwnerClientIds()) {
             // Record the client id with lowest priority that is using the current Cas system.
-            int priority = getOwnerClientPriority(ownerId);
+            int priority = updateAndGetOwnerClientPriority(ownerId);
             if (currentLowestPriority > priority) {
                 lowestPriorityOwnerId = ownerId;
                 currentLowestPriority = priority;
@@ -867,7 +865,7 @@
         }
         for (int ownerId : ciCam.getOwnerClientIds()) {
             // Record the client id with lowest priority that is using the current Cas system.
-            int priority = getOwnerClientPriority(ownerId);
+            int priority = updateAndGetOwnerClientPriority(ownerId);
             if (currentLowestPriority > priority) {
                 lowestPriorityOwnerId = ownerId;
                 currentLowestPriority = priority;
@@ -966,18 +964,17 @@
     }
 
     @VisibleForTesting
-    // This mothod is to sync up the request client's foreground/background status and update
-    // the client priority accordingly whenever new resource request comes in.
-    protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
-        int pid = requestProfile.getProcessId();
-        boolean currentIsForeground = checkIsForeground(pid);
-        if (requestProfile.isForeground() == currentIsForeground) {
+    // This mothod is to sync up the request/holder client's foreground/background status and update
+    // the client priority accordingly whenever a new resource request comes in.
+    protected void clientPriorityUpdateOnRequest(ClientProfile profile) {
+        if (profile.isPriorityOverwritten()) {
             // To avoid overriding the priority set through updateClientPriority API.
             return;
         }
-        requestProfile.setForeground(currentIsForeground);
-        requestProfile.setPriority(
-                getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+        int pid = profile.getProcessId();
+        boolean currentIsForeground = checkIsForeground(pid);
+        profile.setPriority(
+                getClientPriority(profile.getUseCase(), currentIsForeground));
     }
 
     @VisibleForTesting
@@ -1154,13 +1151,15 @@
     }
 
     /**
-     * Get the owner client's priority.
+     * Update and get the owner client's priority.
      *
      * @param clientId the owner client id.
      * @return the priority of the owner client.
      */
-    private int getOwnerClientPriority(int clientId) {
-        return getClientProfile(clientId).getPriority();
+    private int updateAndGetOwnerClientPriority(int clientId) {
+        ClientProfile profile = getClientProfile(clientId);
+        clientPriorityUpdateOnRequest(profile);
+        return profile.getPriority();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index cc41f61..62afcc8 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -1,7 +1,7 @@
 per-file Snappable.java = file:/services/core/java/com/android/server/pm/OWNERS
 per-file Snappable.java = shombert@google.com
-per-file SnapShot* = file:/services/core/java/com/android/server/pm/OWNERS
-per-file SnapShot* = shombert@google.com
+per-file Snapshot* = file:/services/core/java/com/android/server/pm/OWNERS
+per-file Snapshot* = shombert@google.com
 per-file Watchable* = file:/services/core/java/com/android/server/pm/OWNERS
 per-file Watchable* = shombert@google.com
 per-file Watched* = file:/services/core/java/com/android/server/pm/OWNERS
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -3,5 +3,5 @@
 benedictwong@google.com
 ckesting@google.com
 evitayan@google.com
+junyin@google.com
 nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 9c3721b1..382398a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -596,7 +596,12 @@
     /** Retrieves the network score for a VCN Network */
     // Package visibility for use in VcnGatewayConnection and VcnNetworkProvider
     static NetworkScore getNetworkScore() {
-        return new NetworkScore.Builder().setLegacyInt(VCN_LEGACY_SCORE_INT).build();
+        // TODO(b/193687515): Stop setting TRANSPORT_PRIMARY, define a TRANSPORT_VCN, and set in
+        //                    NetworkOffer/NetworkAgent.
+        return new NetworkScore.Builder()
+                .setLegacyInt(VCN_LEGACY_SCORE_INT)
+                .setTransportPrimary(true)
+                .build();
     }
 
     /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7713320..2c56359 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1816,26 +1816,26 @@
 
     @Override
     public void onUnlockUser(final int userId) {
-        TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
-        t.traceBegin("on-unlock-user-" + userId);
-        try {
-            synchronized (mLock) {
-                if (mCurrentUserId == userId) {
-                    if (mWaitingForUnlock) {
-                        // the desired wallpaper is not direct-boot aware, load it now
-                        final WallpaperData systemWallpaper =
-                                getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-                        switchWallpaper(systemWallpaper, null);
-                        notifyCallbacksLocked(systemWallpaper);
-                    }
+        synchronized (mLock) {
+            if (mCurrentUserId == userId) {
+                if (mWaitingForUnlock) {
+                    // the desired wallpaper is not direct-boot aware, load it now
+                    final WallpaperData systemWallpaper =
+                            getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+                    switchWallpaper(systemWallpaper, null);
+                    notifyCallbacksLocked(systemWallpaper);
+                }
 
-                    // Make sure that the SELinux labeling of all the relevant files is correct.
-                    // This corrects for mislabeling bugs that might have arisen from move-to
-                    // operations involving the wallpaper files.  This isn't timing-critical,
-                    // so we do it in the background to avoid holding up the user unlock operation.
-                    if (!mUserRestorecon.get(userId)) {
-                        mUserRestorecon.put(userId, true);
-                        Runnable relabeler = () -> {
+                // Make sure that the SELinux labeling of all the relevant files is correct.
+                // This corrects for mislabeling bugs that might have arisen from move-to
+                // operations involving the wallpaper files.  This isn't timing-critical,
+                // so we do it in the background to avoid holding up the user unlock operation.
+                if (!mUserRestorecon.get(userId)) {
+                    mUserRestorecon.put(userId, true);
+                    Runnable relabeler = () -> {
+                        final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+                        t.traceBegin("Wallpaper_selinux_restorecon-" + userId);
+                        try {
                             final File wallpaperDir = getWallpaperDir(userId);
                             for (String filename : sPerUserFiles) {
                                 File f = new File(wallpaperDir, filename);
@@ -1843,13 +1843,13 @@
                                     SELinux.restorecon(f);
                                 }
                             }
-                        };
-                        BackgroundThread.getHandler().post(relabeler);
-                    }
+                        } finally {
+                            t.traceEnd();
+                        }
+                    };
+                    BackgroundThread.getHandler().post(relabeler);
                 }
             }
-        } finally {
-            t.traceEnd();
         }
     }
 
@@ -1868,7 +1868,7 @@
 
     void switchUser(int userId, IRemoteCallback reply) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
-        t.traceBegin("switch-user-" + userId);
+        t.traceBegin("Wallpaper_switch-user-" + userId);
         try {
             final WallpaperData systemWallpaper;
             final WallpaperData lockWallpaper;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a24319f..fe4eead 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
 import static android.os.Build.IS_USER;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -34,6 +36,7 @@
 import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
 import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
 import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE;
 import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
 import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
 import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
@@ -42,6 +45,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.utils.RegionUtils.forEachRect;
 
+import android.accessibilityservice.AccessibilityTrace;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
@@ -70,7 +74,6 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.util.ArraySet;
-import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
@@ -99,8 +102,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Paths;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -116,21 +117,16 @@
     private static final String TAG = AccessibilityController.class.getSimpleName();
 
     private static final Object STATIC_LOCK = new Object();
-    static AccessibilityControllerInternal
+    static AccessibilityControllerInternalImpl
             getAccessibilityControllerInternal(WindowManagerService service) {
         return AccessibilityControllerInternalImpl.getInstance(service);
     }
 
-    private final AccessibilityTracing mAccessibilityTracing;
+    private final AccessibilityControllerInternalImpl mAccessibilityTracing;
     private final WindowManagerService mService;
     private static final Rect EMPTY_RECT = new Rect();
     private static final float[] sTempFloats = new float[9];
 
-    AccessibilityController(WindowManagerService service) {
-        mService = service;
-        mAccessibilityTracing = AccessibilityTracing.getInstance(service);
-    }
-
     private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
     private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
             new SparseArray<>();
@@ -138,10 +134,17 @@
     // Set to true if initializing window population complete.
     private boolean mAllObserversInitialized = true;
 
+    AccessibilityController(WindowManagerService service) {
+        mService = service;
+        mAccessibilityTracing =
+                AccessibilityController.getAccessibilityControllerInternal(service);
+    }
+
     boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(
                     TAG + ".setMagnificationCallbacks",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "displayId=" + displayId + "; callbacks={" + callbacks + "}");
         }
         boolean result = false;
@@ -176,54 +179,32 @@
      *
      * @param displayId The logical display id.
      * @param callback The callback.
-     * @return {@code false} if display id is not valid or an embedded display.
      */
-    boolean setWindowsForAccessibilityCallback(int displayId,
+    void setWindowsForAccessibilityCallback(int displayId,
             WindowsForAccessibilityCallback callback) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(
                     TAG + ".setWindowsForAccessibilityCallback",
+                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                     "displayId=" + displayId + "; callback={" + callback + "}");
         }
-        final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
-        if (dc == null) {
-            return false;
-        }
 
         if (callback != null) {
             WindowsForAccessibilityObserver observer =
                     mWindowsForAccessibilityObserver.get(displayId);
-            if (isEmbeddedDisplay(dc)) {
-                // If this display is an embedded one, its window observer should have been set from
-                // window manager after setting its parent window. But if its window observer is
-                // empty, that means this mapping didn't be set, and needs to do this again.
-                // This happened when accessibility window observer is disabled and enabled again.
-                if (observer == null) {
-                    handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
-                }
-                return false;
-            } else if (observer != null) {
+            if (observer != null) {
                 final String errorMessage = "Windows for accessibility callback of display "
                         + displayId + " already set!";
                 Slog.e(TAG, errorMessage);
                 if (Build.IS_DEBUGGABLE) {
                     throw new IllegalStateException(errorMessage);
                 }
-                removeObserverOfEmbeddedDisplay(observer);
                 mWindowsForAccessibilityObserver.remove(displayId);
             }
             observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
             mWindowsForAccessibilityObserver.put(displayId, observer);
             mAllObserversInitialized &= observer.mInitialized;
         } else {
-            if (isEmbeddedDisplay(dc)) {
-                // If this display is an embedded one, its window observer should be removed along
-                // with the window observer of its parent display removed because the window
-                // observer of the embedded display and its parent display is the same, and would
-                // be removed together when stopping the window tracking of its parent display. So
-                // here don't need to do removing window observer of the embedded display again.
-                return true;
-            }
             final WindowsForAccessibilityObserver windowsForA11yObserver =
                     mWindowsForAccessibilityObserver.get(displayId);
             if (windowsForA11yObserver == null) {
@@ -234,16 +215,15 @@
                     throw new IllegalStateException(errorMessage);
                 }
             }
-            removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
             mWindowsForAccessibilityObserver.remove(displayId);
         }
-        return true;
     }
 
     void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(
                     TAG + ".performComputeChangedWindowsNot",
+                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                     "displayId=" + displayId + "; forceSend=" + forceSend);
         }
         WindowsForAccessibilityObserver observer = null;
@@ -260,8 +240,10 @@
     }
 
     void setMagnificationSpec(int displayId, MagnificationSpec spec) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec",
+                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                     "displayId=" + displayId + "; spec={" + spec + "}");
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -276,8 +258,9 @@
     }
 
     void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
                             + "}");
         }
@@ -288,9 +271,10 @@
     }
 
     void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(
                     TAG + ".onRectangleOnScreenRequested",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "displayId=" + displayId + "; rectangle={" + rectangle + "}");
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -301,9 +285,11 @@
     }
 
     void onWindowLayersChanged(int displayId) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
-                    TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
+                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+                    "displayId=" + displayId);
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
@@ -316,15 +302,18 @@
         }
     }
 
-    void onRotationChanged(DisplayContent displayContent) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+    void onDisplaySizeChanged(DisplayContent displayContent) {
+
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onRotationChanged",
+                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                     "displayContent={" + displayContent + "}");
         }
         final int displayId = displayContent.getDisplayId();
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onRotationChanged(displayContent);
+            displayMagnifier.onDisplaySizeChanged(displayContent);
         }
         final WindowsForAccessibilityObserver windowsForA11yObserver =
                 mWindowsForAccessibilityObserver.get(displayId);
@@ -334,8 +323,9 @@
     }
 
     void onAppWindowTransition(int displayId, int transition) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "displayId=" + displayId + "; transition=" + transition);
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -346,8 +336,10 @@
     }
 
     void onWindowTransition(WindowState windowState, int transition) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onWindowTransition",
+                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                     "windowState={" + windowState + "}; transition=" + transition);
         }
         final int displayId = windowState.getDisplayId();
@@ -364,9 +356,9 @@
 
     void onWindowFocusChangedNot(int displayId) {
         // Not relevant for the display magnifier.
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
-                    TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot",
+                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId);
         }
         WindowsForAccessibilityObserver observer = null;
         synchronized (mService.mGlobalLock) {
@@ -426,12 +418,10 @@
     }
 
     void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
-                    TAG + ".onSomeWindowResizedOrMoved",
-                    "displayIds={" + displayIds.toString() + "}",
-                    "".getBytes(),
-                    callingUid);
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
+                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+                    "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
         }
         // Not relevant for the display magnifier.
         for (int i = 0; i < displayIds.length; i++) {
@@ -444,9 +434,10 @@
     }
 
     void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(
                     TAG + ".drawMagnifiedRegionBorderIfNeeded",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "displayId=" + displayId + "; transaction={" + t + "}");
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -457,8 +448,9 @@
     }
 
     MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+                    FLAGS_MAGNIFICATION_CALLBACK,
                     "windowState={" + windowState + "}");
         }
         final int displayId = windowState.getDisplayId();
@@ -470,17 +462,19 @@
     }
 
     boolean hasCallbacks() {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".hasCallbacks",
+                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
         }
         return (mDisplayMagnifiers.size() > 0
                 || mWindowsForAccessibilityObserver.size() > 0);
     }
 
     void setForceShowMagnifiableBounds(int displayId, boolean show) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
-                    "displayId=" + displayId + "; show=" + show);
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
+                    FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
         }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
@@ -489,47 +483,10 @@
         }
     }
 
-    void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
-            WindowState parentWindow) {
-        handleWindowObserverOfEmbeddedDisplay(
-                embeddedDisplayId, parentWindow, Binder.getCallingUid());
-    }
-
-    void handleWindowObserverOfEmbeddedDisplay(
-            int embeddedDisplayId, WindowState parentWindow, int callingUid) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
-                    "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
-                    + parentWindow + "}",
-                    "".getBytes(),
-                    callingUid);
-        }
-        if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
-            return;
-        }
-        // Finds the parent display of this embedded display
-        final int parentDisplayId;
-        WindowState candidate = parentWindow;
-        while (candidate != null) {
-            parentWindow = candidate;
-            candidate = parentWindow.getDisplayContent().getParentWindow();
-        }
-        parentDisplayId = parentWindow.getDisplayId();
-        // Uses the observer of parent display
-        final WindowsForAccessibilityObserver windowsForA11yObserver =
-                mWindowsForAccessibilityObserver.get(parentDisplayId);
-
-        if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
-            // Replaces the observer of embedded display to the one of parent display
-            mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
-        }
-    }
-
     void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
-        if (mAccessibilityTracing.isEnabled()) {
-            mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
-                    "windowState=" + windowState + "; shown=" + shown);
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".onImeSurfaceShownChanged",
+                    FLAGS_MAGNIFICATION_CALLBACK, "windowState=" + windowState + ";shown=" + shown);
         }
         final int displayId = windowState.getDisplayId();
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -555,23 +512,6 @@
                 + "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
     }
 
-    private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
-            observerOfParentDisplay) {
-        final IntArray embeddedDisplayIdList =
-                observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
-
-        for (int index = 0; index < embeddedDisplayIdList.size(); index++) {
-            final int embeddedDisplayId = embeddedDisplayIdList.get(index);
-            mWindowsForAccessibilityObserver.remove(embeddedDisplayId);
-        }
-    }
-
-    private static boolean isEmbeddedDisplay(DisplayContent dc) {
-        final Display display = dc.getDisplay();
-
-        return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null;
-    }
-
     /**
      * This class encapsulates the functionality related to display magnification.
      */
@@ -580,7 +520,7 @@
         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
 
         private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
-        private static final boolean DEBUG_ROTATION = false;
+        private static final boolean DEBUG_DISPLAY_SIZE = false;
         private static final boolean DEBUG_LAYERS = false;
         private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
         private static final boolean DEBUG_VIEWPORT_WINDOW = false;
@@ -599,7 +539,7 @@
         private final Handler mHandler;
         private final DisplayContent mDisplayContent;
         private final Display mDisplay;
-        private final AccessibilityTracing mAccessibilityTracing;
+        private final AccessibilityControllerInternalImpl mAccessibilityTracing;
 
         private final MagnificationCallbacks mCallbacks;
 
@@ -618,11 +558,13 @@
             mDisplay = display;
             mHandler = new MyHandler(mService.mH.getLooper());
             mMagnifedViewport = new MagnifiedViewport();
-            mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+            mAccessibilityTracing =
+                    AccessibilityController.getAccessibilityControllerInternal(mService);
             mLongAnimationDuration = mDisplayContext.getResources().getInteger(
                     com.android.internal.R.integer.config_longAnimTime);
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
+                        FLAGS_MAGNIFICATION_CALLBACK,
                         "windowManagerService={" + windowManagerService + "}; displayContent={"
                                 + displayContent + "}; display={" + display + "}; callbacks={"
                                 + callbacks + "}");
@@ -630,9 +572,9 @@
         }
 
         void setMagnificationSpec(MagnificationSpec spec) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec",
+                        FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
             }
             mMagnifedViewport.updateMagnificationSpec(spec);
             mMagnifedViewport.recomputeBounds();
@@ -642,25 +584,26 @@
         }
 
         void setForceShowMagnifiableBounds(boolean show) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
+                        FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
             }
             mForceShowMagnifiableBounds = show;
             mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
         }
 
         boolean isForceShowingMagnifiableBounds() {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
+                        FLAGS_MAGNIFICATION_CALLBACK);
             }
             return mForceShowMagnifiableBounds;
         }
 
         void onRectangleOnScreenRequested(Rect rectangle) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
+                        FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
             }
             if (DEBUG_RECTANGLE_REQUESTED) {
                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -683,8 +626,9 @@
         }
 
         void onWindowLayersChanged() {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(
+                        LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
             }
             if (DEBUG_LAYERS) {
                 Slog.i(LOG_TAG, "Layers changed.");
@@ -693,23 +637,24 @@
             mService.scheduleAnimationLocked();
         }
 
-        void onRotationChanged(DisplayContent displayContent) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+        void onDisplaySizeChanged(DisplayContent displayContent) {
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged",
+                        FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}");
             }
-            if (DEBUG_ROTATION) {
+            if (DEBUG_DISPLAY_SIZE) {
                 final int rotation = displayContent.getRotation();
                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
                         + " displayId: " + displayContent.getDisplayId());
             }
-            mMagnifedViewport.onRotationChanged();
-            mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+            mMagnifedViewport.onDisplaySizeChanged();
+            mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
         }
 
         void onAppWindowTransition(int displayId, int transition) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition",
+                        FLAGS_MAGNIFICATION_CALLBACK,
                         "displayId=" + displayId + "; transition=" + transition);
             }
             if (DEBUG_WINDOW_TRANSITIONS) {
@@ -733,8 +678,9 @@
         }
 
         void onWindowTransition(WindowState windowState, int transition) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition",
+                        FLAGS_MAGNIFICATION_CALLBACK,
                         "windowState={" + windowState + "}; transition=" + transition);
             }
             if (DEBUG_WINDOW_TRANSITIONS) {
@@ -791,18 +737,18 @@
         }
 
         void onImeSurfaceShownChanged(boolean shown) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".onImeSurfaceShownChanged",
+                        FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown);
             }
             mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
                     shown ? 1 : 0, 0).sendToTarget();
         }
 
         MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
-                        "windowState={" + windowState + "}");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow",
+                        FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}");
             }
             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
             if (spec != null && !spec.isNop()) {
@@ -814,8 +760,9 @@
         }
 
         void getMagnificationRegion(Region outMagnificationRegion) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
+                        FLAGS_MAGNIFICATION_CALLBACK,
                         "outMagnificationRegion={" + outMagnificationRegion + "}");
             }
             // Make sure we're working with the most current bounds
@@ -824,25 +771,26 @@
         }
 
         void destroy() {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
             }
             mMagnifedViewport.destroyWindow();
         }
 
         // Can be called outside of a surface transaction
         void showMagnificationBoundsIfNeeded() {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
+                        FLAGS_MAGNIFICATION_CALLBACK);
             }
             mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
                     .sendToTarget();
         }
 
         void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
-                        "transition={" + t + "}");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+                        FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
             }
             mMagnifedViewport.drawWindowIfNeeded(t);
         }
@@ -887,7 +835,8 @@
 
                 if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
                     mCircularPath = new Path();
-                    mDisplay.getRealSize(mScreenSize);
+
+                    getDisplaySizeLocked(mScreenSize);
                     final int centerXY = mScreenSize.x / 2;
                     mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
                 } else {
@@ -917,7 +866,7 @@
             }
 
             void recomputeBounds() {
-                mDisplay.getRealSize(mScreenSize);
+                getDisplaySizeLocked(mScreenSize);
                 final int screenWidth = mScreenSize.x;
                 final int screenHeight = mScreenSize.y;
 
@@ -1052,9 +1001,10 @@
                         || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
             }
 
-            void onRotationChanged() {
+            void onDisplaySizeChanged() {
                 // If we are showing the magnification border, hide it immediately so
-                // the user does not see strange artifacts during rotation. The screenshot
+                // the user does not see strange artifacts during display size changed caused by
+                // rotation or folding/unfolding the device. In the rotation case, the screenshot
                 // used for rotation already has the border. After the rotation is complete
                 // we will show the border.
                 if (isMagnifying() || isForceShowingMagnifiableBounds()) {
@@ -1112,6 +1062,12 @@
                 }, false /* traverseTopToBottom */ );
             }
 
+            private void getDisplaySizeLocked(Point outSize) {
+                final Rect bounds =
+                        mDisplayContent.getConfiguration().windowConfiguration.getBounds();
+                outSize.set(bounds.width(), bounds.height());
+            }
+
             void dump(PrintWriter pw, String prefix) {
                 mWindow.dump(pw, prefix);
             }
@@ -1226,7 +1182,7 @@
 
                 void updateSize() {
                     synchronized (mService.mGlobalLock) {
-                        mDisplay.getRealSize(mScreenSize);
+                        getDisplaySizeLocked(mScreenSize);
                         mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
                                 PixelFormat.RGBA_8888);
                         invalidate(mDirtyRect);
@@ -1365,7 +1321,7 @@
             public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
             public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
             public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
-            public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+            public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
             public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
             public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
 
@@ -1397,9 +1353,8 @@
                         mCallbacks.onUserContextChanged();
                     } break;
 
-                    case MESSAGE_NOTIFY_ROTATION_CHANGED: {
-                        final int rotation = message.arg1;
-                        mCallbacks.onRotationChanged(rotation);
+                    case MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED: {
+                        mCallbacks.onDisplaySizeChanged();
                     } break;
 
                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
@@ -1482,7 +1437,7 @@
 
         private final Handler mHandler;
 
-        private final AccessibilityTracing mAccessibilityTracing;
+        private final AccessibilityControllerInternalImpl mAccessibilityTracing;
 
         private final WindowsForAccessibilityCallback mCallback;
 
@@ -1490,8 +1445,6 @@
 
         private final long mRecurringAccessibilityEventsIntervalMillis;
 
-        private final IntArray mEmbeddedDisplayIdList = new IntArray(0);
-
         // Set to true if initializing window population complete.
         private boolean mInitialized;
 
@@ -1502,24 +1455,26 @@
             mCallback = callback;
             mDisplayId = displayId;
             mHandler = new MyHandler(mService.mH.getLooper());
-            mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+            mAccessibilityTracing =
+                    AccessibilityController.getAccessibilityControllerInternal(mService);
             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                     .getSendRecurringAccessibilityEventsInterval();
             computeChangedWindows(true);
         }
 
         void performComputeChangedWindows(boolean forceSend) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
-                        "forceSend=" + forceSend);
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows",
+                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
             }
             mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
             computeChangedWindows(forceSend);
         }
 
         void scheduleComputeChangedWindows() {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows",
+                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
             }
             if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
@@ -1527,21 +1482,6 @@
             }
         }
 
-        IntArray getAndClearEmbeddedDisplayIdList() {
-            final IntArray returnedArray = new IntArray(mEmbeddedDisplayIdList.size());
-            returnedArray.addAll(mEmbeddedDisplayIdList);
-            mEmbeddedDisplayIdList.clear();
-
-            return returnedArray;
-        }
-
-        void addEmbeddedDisplay(int displayId) {
-            if (displayId == mDisplayId) {
-                return;
-            }
-            mEmbeddedDisplayIdList.add(displayId);
-        }
-
         boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
             int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
             int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
@@ -1594,9 +1534,9 @@
          * @param forceSend Send the windows the accessibility even if they haven't changed.
          */
         void computeChangedWindows(boolean forceSend) {
-            if (mAccessibilityTracing.isEnabled()) {
-                mAccessibilityTracing.logState(
-                        LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows",
+                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
             }
             if (DEBUG) {
                 Slog.i(LOG_TAG, "computeChangedWindows()");
@@ -1708,12 +1648,7 @@
                 addedWindows.clear();
 
                 // Gets the top focused display Id and window token for supporting multi-display.
-                // If this top focused display is an embedded one, using its parent display as the
-                // top focused display.
-                final DisplayContent topFocusedDisplayContent =
-                        mService.mRoot.getTopFocusedDisplayContent();
-                topFocusedDisplayId = isEmbeddedDisplay(topFocusedDisplayContent) ? mDisplayId
-                        : topFocusedDisplayContent.getDisplayId();
+                topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
                 topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
             }
             mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
@@ -1917,8 +1852,6 @@
         public String toString() {
             return "WindowsForAccessibilityObserver{"
                     + "mDisplayId=" + mDisplayId
-                    + ", mEmbeddedDisplayIdList="
-                    + Arrays.toString(mEmbeddedDisplayIdList.toArray())
                     + ", mInitialized=" + mInitialized
                     + '}';
         }
@@ -1945,8 +1878,8 @@
     private static final class AccessibilityControllerInternalImpl
             implements AccessibilityControllerInternal {
 
-        private static AccessibilityControllerInternal sInstance;
-        static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+        private static AccessibilityControllerInternalImpl sInstance;
+        static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) {
             synchronized (STATIC_LOCK) {
                 if (sInstance == null) {
                     sInstance = new AccessibilityControllerInternalImpl(service);
@@ -1956,18 +1889,23 @@
         }
 
         private final AccessibilityTracing mTracing;
+        private volatile long mEnabledTracingFlags;
+
         private AccessibilityControllerInternalImpl(WindowManagerService service) {
             mTracing = AccessibilityTracing.getInstance(service);
+            mEnabledTracingFlags = 0L;
         }
 
         @Override
-        public void startTrace() {
+        public void startTrace(long loggingTypes) {
+            mEnabledTracingFlags = loggingTypes;
             mTracing.startTrace();
         }
 
         @Override
         public void stopTrace() {
             mTracing.stopTrace();
+            mEnabledTracingFlags = 0L;
         }
 
         @Override
@@ -1975,19 +1913,37 @@
             return mTracing.isEnabled();
         }
 
-        @Override
-        public void logTrace(
-                String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] stackTrace) {
-            mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+        boolean isTracingEnabled(long flags) {
+            return (flags & mEnabledTracingFlags) != 0L;
+        }
+
+        void logTrace(String where, long loggingTypes) {
+            logTrace(where, loggingTypes, "");
+        }
+
+        void logTrace(String where, long loggingTypes, String callingParams) {
+            logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid());
+        }
+
+        void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid) {
+            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid,
+                    new HashSet<String>(Arrays.asList("logTrace")));
         }
 
         @Override
-        public void logTrace(
-                String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] callStack, long timeStamp, int processId, long threadId) {
-            mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp,
-                    processId, threadId);
+        public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
+            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
+                    ignoreStackEntries);
+        }
+
+        @Override
+        public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] callStack, long timeStamp, int processId,
+                long threadId, Set<String> ignoreStackEntries) {
+            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
+                    timeStamp, processId, threadId, ignoreStackEntries);
         }
     }
 
@@ -2004,7 +1960,6 @@
 
         private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
         private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
-        private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
         private static final String TAG = "AccessibilityTracing";
         private static final long MAGIC_NUMBER_VALUE =
                 ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
@@ -2034,13 +1989,6 @@
                 return;
             }
             synchronized (mLock) {
-                try {
-                    Files.createDirectories(Paths.get(TRACE_DIRECTORY));
-                    mTraceFile.createNewFile();
-                } catch (Exception e) {
-                    Slog.e(TAG, "Error: Failed to create trace file.");
-                    return;
-                }
                 mEnabled = true;
                 mBuffer.resetBuffer();
             }
@@ -2071,86 +2019,127 @@
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(String where) {
+        void logState(String where, long loggingTypes) {
             if (!mEnabled) {
                 return;
             }
-            logState(where, "");
+            logState(where, loggingTypes, "");
         }
 
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(String where, String callingParams) {
+        void logState(String where, long loggingTypes, String callingParams) {
             if (!mEnabled) {
                 return;
             }
-            logState(where, callingParams, "".getBytes());
+            logState(where, loggingTypes, callingParams, "".getBytes());
         }
 
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(String where, String callingParams, byte[] a11yDump) {
+        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) {
             if (!mEnabled) {
                 return;
             }
-            logState(where, callingParams, a11yDump, Binder.getCallingUid());
+            logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(),
+                    new HashSet<String>(Arrays.asList("logState")));
         }
 
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(
-                String where, String callingParams, byte[] a11yDump, int callingUid) {
+        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, Set<String> ignoreStackEntries) {
             if (!mEnabled) {
                 return;
             }
             StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
-
-            logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+            ignoreStackEntries.add("logState");
+            logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements,
+                    ignoreStackEntries);
         }
 
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] stackTrace) {
+        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
             if (!mEnabled) {
                 return;
             }
-
-            log(where, callingParams, a11yDump, callingUid, stackTrace,
+            log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
                     SystemClock.elapsedRealtimeNanos(),
                     Process.myPid() + ":" + Application.getProcessName(),
-                    Thread.currentThread().getId() + ":" + Thread.currentThread().getName());
+                    Thread.currentThread().getId() + ":" + Thread.currentThread().getName(),
+                    ignoreStackEntries);
         }
 
         /**
          * Write an accessibility trace log entry.
          */
-        void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) {
+        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId,
+                long threadId, Set<String> ignoreStackEntries) {
             if (!mEnabled) {
                 return;
             }
-            log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp,
-                    String.valueOf(processId), String.valueOf(threadId));
+            log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp,
+                    String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries);
         }
 
-        private  String toStackTraceString(StackTraceElement[] stackTraceElements) {
+        private  String toStackTraceString(StackTraceElement[] stackTraceElements,
+                Set<String> ignoreStackEntries) {
+
             if (stackTraceElements == null) {
                 return "";
             }
+
             StringBuilder stringBuilder = new StringBuilder();
-            boolean skip = true;
-            for (int i = 0; i < stackTraceElements.length; i++) {
-                if (stackTraceElements[i].toString().contains(
-                            AccessibilityTracing.class.getSimpleName())) {
-                    skip = false;
-                } else if (!skip) {
-                    stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+            int i = 0;
+
+            // Skip the first a few elements until after any ignoreStackEntries
+            int firstMatch = -1;
+            while (i < stackTraceElements.length) {
+                for (String ele : ignoreStackEntries) {
+                    if (stackTraceElements[i].toString().contains(ele)) {
+                        // found the first stack element containing the ignorable stack entries
+                        firstMatch = i;
+                        break;
+                    }
                 }
+                if (firstMatch < 0) {
+                    // Haven't found the first match yet, continue
+                    i++;
+                } else {
+                    break;
+                }
+            }
+            int lastMatch = firstMatch;
+            if (i < stackTraceElements.length) {
+                i++;
+                // Found the first match. Now look for the last match.
+                while (i < stackTraceElements.length) {
+                    for (String ele : ignoreStackEntries) {
+                        if (stackTraceElements[i].toString().contains(ele)) {
+                            // This is a match. Look at the next stack element.
+                            lastMatch = i;
+                            break;
+                        }
+                    }
+                    if (lastMatch != i) {
+                        // Found a no-match.
+                        break;
+                    }
+                    i++;
+                }
+            }
+
+            i = lastMatch + 1;
+            while (i < stackTraceElements.length) {
+                stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+                i++;
             }
             return stringBuilder.toString();
         }
@@ -2158,19 +2147,22 @@
         /**
          * Write the current state to the buffer
          */
-        private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] callingStack, long timeStamp, String processName,
-                String threadName) {
+        private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] callingStack, long timeStamp,
+                String processName, String threadName, Set<String> ignoreStackEntries) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = timeStamp;
-            args.arg2 = where;
-            args.arg3 = processName;
-            args.arg4 = threadName;
-            args.arg5 = callingUid;
-            args.arg6 = callingParams;
-            args.arg7 = callingStack;
-            args.arg8 = a11yDump;
-            mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+            args.arg2 = loggingTypes;
+            args.arg3 = where;
+            args.arg4 = processName;
+            args.arg5 = threadName;
+            args.arg6 = ignoreStackEntries;
+            args.arg7 = callingParams;
+            args.arg8 = callingStack;
+            args.arg9 = a11yDump;
+
+            mHandler.obtainMessage(
+                    LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget();
         }
 
         /**
@@ -2199,8 +2191,6 @@
                                     LocalServices.getService(PackageManagerInternal.class);
 
                             long tokenOuter = os.start(ENTRY);
-                            String callingStack =
-                                    toStackTraceString((StackTraceElement[]) args.arg7);
 
                             long reportedTimeStampNanos = (long) args.arg1;
                             long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -2213,13 +2203,25 @@
 
                             os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos);
                             os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString());
-                            os.write(WHERE, (String) args.arg2);
-                            os.write(PROCESS_NAME, (String) args.arg3);
-                            os.write(THREAD_ID_NAME, (String) args.arg4);
-                            os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5));
-                            os.write(CALLING_PARAMS, (String) args.arg6);
+
+                            long loggingTypes = (long) args.arg2;
+                            List<String> loggingTypeNames =
+                                    AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes);
+
+                            for (String type : loggingTypeNames) {
+                                os.write(LOGGING_TYPE, type);
+                            }
+                            os.write(WHERE, (String) args.arg3);
+                            os.write(PROCESS_NAME, (String) args.arg4);
+                            os.write(THREAD_ID_NAME, (String) args.arg5);
+                            os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1));
+                            os.write(CALLING_PARAMS, (String) args.arg7);
+
+                            String callingStack = toStackTraceString(
+                                    (StackTraceElement[]) args.arg8, (Set<String>) args.arg6);
+
                             os.write(CALLING_STACKS, callingStack);
-                            os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8);
+                            os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
 
                             long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
                             synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 3a4faf7..1f5ff25 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -28,6 +28,11 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -35,10 +40,9 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -68,6 +72,7 @@
 import android.util.Slog;
 import android.view.RemoteAnimationDefinition;
 import android.window.SizeConfigurationBuckets;
+import android.window.TransitionInfo;
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -188,7 +193,7 @@
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
             r = ActivityRecord.isInRootTaskLocked(token);
             if (r != null) {
-                if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+                if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
                     // The activity was requested to restart from
                     // {@link #restartActivityProcessIfVisible}.
                     restartingName = r.app.mName;
@@ -535,6 +540,27 @@
     }
 
     @Override
+    @Nullable
+    public IBinder getActivityTokenBelow(IBinder activityToken) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken);
+                if (ar == null) {
+                    return null;
+                }
+                final ActivityRecord below = ar.getTask().getActivityBelow(ar);
+                if (below != null && below.getUid() == ar.getUid()) {
+                    return below.appToken.asBinder();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return null;
+    }
+
+    @Override
     public ComponentName getCallingActivity(IBinder token) {
         synchronized (mGlobalLock) {
             final ActivityRecord r = getCallingRecord(token);
@@ -1010,10 +1036,14 @@
         final long origId = Binder.clearCallingIdentity();
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-            if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
+            if (r != null && r.isState(RESUMED, PAUSING)) {
                 r.mDisplayContent.mAppTransition.overridePendingAppTransition(
                         packageName, enterAnim, exitAnim, null, null,
                         r.mOverrideTaskTransition);
+                mService.getTransitionController().setOverrideAnimation(
+                        TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
+                                enterAnim, exitAnim, r.mOverrideTaskTransition),
+                        null /* startCallback */, null /* finishCallback */);
             }
         }
         Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0f6a718..b1163c4 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -300,9 +300,12 @@
                 return;
             }
             if (mLastLaunchedActivity != null) {
-                // Transfer the launch cookie because it is a consecutive launch event.
+                // Transfer the launch cookie and launch root task because it is a consecutive
+                // launch event.
                 r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
                 mLastLaunchedActivity.mLaunchCookie = null;
+                r.mLaunchRootTask = mLastLaunchedActivity.mLaunchRootTask;
+                mLastLaunchedActivity.mLaunchRootTask = null;
             }
             mLastLaunchedActivity = r;
             if (!r.noDisplay && !r.isReportedDrawn()) {
@@ -775,7 +778,7 @@
             Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
                     + " state=" + r.getState() + " finishing=" + r.finishing);
         }
-        if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) {
+        if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) {
             // The activity may be launching while keyguard is locked. The keyguard may be dismissed
             // after the activity finished relayout, so skip the visibility check to avoid aborting
             // the tracking of launch event.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 22bff21..f3daf07 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -129,6 +129,17 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
 import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
@@ -191,18 +202,7 @@
 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;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-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.RESTARTING_PROCESS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -273,6 +273,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -303,6 +304,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
@@ -316,12 +318,14 @@
 import android.window.SplashScreenView;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
 import android.window.TaskSnapshot;
+import android.window.TransitionInfo.AnimationOptions;
 import android.window.WindowContainerToken;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
@@ -338,7 +342,6 @@
 import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.Task.ActivityState;
 import com.android.server.wm.WindowManagerService.H;
 import com.android.server.wm.utils.InsetUtils;
 
@@ -347,6 +350,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -490,7 +494,7 @@
     ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
     UriPermissionOwner uriPermissions; // current special URI access perms.
     WindowProcessController app;      // if non-null, hosting application
-    private ActivityState mState;    // current state we are in
+    private State mState;    // current state we are in
     private Bundle mIcicle;         // last saved activity state
     private PersistableBundle mPersistentState; // last persistently saved activity state
     private boolean mHaveState = true; // Indicates whether the last saved state of activity is
@@ -540,11 +544,6 @@
     final ActivityTaskSupervisor mTaskSupervisor;
     final RootWindowContainer mRootWindowContainer;
 
-    static final int STARTING_WINDOW_NOT_SHOWN = 0;
-    static final int STARTING_WINDOW_SHOWN = 1;
-    static final int STARTING_WINDOW_REMOVED = 2;
-    int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
-
     // Tracking splash screen status from previous activity
     boolean mSplashScreenStyleEmpty = false;
 
@@ -552,6 +551,21 @@
     static final int LAUNCH_SOURCE_TYPE_HOME = 2;
     static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
     static final int LAUNCH_SOURCE_TYPE_APPLICATION = 4;
+
+    enum State {
+        INITIALIZING,
+        STARTED,
+        RESUMED,
+        PAUSING,
+        PAUSED,
+        STOPPING,
+        STOPPED,
+        FINISHING,
+        DESTROYING,
+        DESTROYED,
+        RESTARTING_PROCESS
+    }
+
     /**
      * The type of launch source.
      */
@@ -648,6 +662,14 @@
     boolean allDrawn;
     private boolean mLastAllDrawn;
 
+    /**
+     * Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
+     * Actiivty was part of a syncset, all windows were ready by the time the sync was ready (vs.
+     * only the top-occluding ones). The assumption here is if some were not ready, they were
+     * covered with starting-window/splash-screen.
+     */
+    boolean mLastAllReadyAtSync = false;
+
     private boolean mLastContainsShowWhenLockedWindow;
     private boolean mLastContainsDismissKeyguardWindow;
     private boolean mLastContainsTurnScreenOnWindow;
@@ -682,6 +704,8 @@
     private boolean mInSizeCompatModeForBounds = false;
 
     // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio().
+    // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration
+    // but that isn't reflected in this boolean.
     private boolean mIsAspectRatioApplied = false;
 
     // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
@@ -791,6 +815,7 @@
      */
     private final Configuration mTmpConfig = new Configuration();
     private final Rect mTmpBounds = new Rect();
+    private final Rect mTmpOutNonDecorBounds = new Rect();
 
     // Token for targeting this activity for assist purposes.
     final Binder assistToken = new Binder();
@@ -802,6 +827,9 @@
     // Tracking cookie for the launch of this activity and it's task.
     IBinder mLaunchCookie;
 
+    // Tracking indicated launch root in order to propagate it among trampoline activities.
+    WindowContainerToken mLaunchRootTask;
+
     // Entering PiP is usually done in two phases, we put the task into pinned mode first and
     // SystemUi sets the pinned mode on activity after transition is done.
     boolean mWaitForEnteringPinnedMode;
@@ -856,19 +884,6 @@
         }
     };
 
-    private static String startingWindowStateToString(int state) {
-        switch (state) {
-            case STARTING_WINDOW_NOT_SHOWN:
-                return "STARTING_WINDOW_NOT_SHOWN";
-            case STARTING_WINDOW_SHOWN:
-                return "STARTING_WINDOW_SHOWN";
-            case STARTING_WINDOW_REMOVED:
-                return "STARTING_WINDOW_REMOVED";
-            default:
-                return "unknown state=" + state;
-        }
-    }
-
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final long now = SystemClock.uptimeMillis();
@@ -1012,6 +1027,11 @@
             pw.print("launchCookie=");
             pw.println(mLaunchCookie);
         }
+        if (mLaunchRootTask != null) {
+            pw.print(prefix);
+            pw.print("mLaunchRootTask=");
+            pw.println(mLaunchRootTask);
+        }
         pw.print(prefix); pw.print("mHaveState="); pw.print(mHaveState);
                 pw.print(" mIcicle="); pw.println(mIcicle);
         pw.print(prefix); pw.print("state="); pw.print(mState);
@@ -1020,9 +1040,7 @@
                 pw.print(" finishing="); pw.println(finishing);
         pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
                 pw.print(" inHistory="); pw.print(inHistory);
-                pw.print(" idle="); pw.print(idle);
-                pw.print(" mStartingWindowState=");
-                pw.println(startingWindowStateToString(mStartingWindowState));
+                pw.print(" idle="); pw.println(idle);
         pw.print(prefix); pw.print("occludesParent="); pw.print(occludesParent());
                 pw.print(" noDisplay="); pw.print(noDisplay);
                 pw.print(" immersive="); pw.print(immersive);
@@ -1140,6 +1158,76 @@
         mLetterboxUiController.dump(pw, prefix);
     }
 
+    static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
+            String prefix, String label, boolean complete, boolean brief, boolean client,
+            String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
+        if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+            return false;
+        }
+
+        final boolean full = !brief && (complete || !r.isInHistory());
+        if (needNL) {
+            pw.println("");
+        }
+        if (header != null) {
+            header.run();
+        }
+
+        String innerPrefix = prefix + "      ";
+        String[] args = new String[0];
+        if (lastTask != r.getTask()) {
+            lastTask = r.getTask();
+            pw.print(prefix);
+            pw.print(full ? "* " : "  ");
+            pw.println(lastTask);
+            if (full) {
+                lastTask.dump(pw, prefix + "  ");
+            } else if (complete) {
+                // Complete + brief == give a summary.  Isn't that obvious?!?
+                if (lastTask.intent != null) {
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.println(lastTask.intent.toInsecureString());
+                }
+            }
+        }
+        pw.print(prefix); pw.print(full ? "* " : "    "); pw.print(label);
+        pw.print(" #"); pw.print(index); pw.print(": ");
+        pw.println(r);
+        if (full) {
+            r.dump(pw, innerPrefix, true /* dumpAll */);
+        } else if (complete) {
+            // Complete + brief == give a summary.  Isn't that obvious?!?
+            pw.print(innerPrefix);
+            pw.println(r.intent.toInsecureString());
+            if (r.app != null) {
+                pw.print(innerPrefix);
+                pw.println(r.app);
+            }
+        }
+        if (client && r.attachedToProcess()) {
+            // flush anything that is already in the PrintWriter since the thread is going
+            // to write to the file descriptor directly
+            pw.flush();
+            try {
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.app.getThread().dumpActivity(
+                            tp.getWriteFd(), r.appToken, innerPrefix, args);
+                    // Short timeout, since blocking here can deadlock with the application.
+                    tp.go(fd, 2000);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+            } catch (RemoteException e) {
+                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+            }
+        }
+        return true;
+    }
+
     void setAppTimeTracker(AppTimeTracker att) {
         appTimeTracker = att;
     }
@@ -1249,11 +1337,10 @@
                 updatePictureInPictureMode(null, false);
             } else {
                 mLastReportedMultiWindowMode = inMultiWindowMode;
-                computeConfigurationAfterMultiWindowModeChange();
                 // If the activity is in stopping or stopped state, for instance, it's in the
                 // split screen task and not the top one, the last configuration it should keep
                 // is the one before multi-window mode change.
-                final ActivityState state = getState();
+                final State state = getState();
                 if (state != STOPPED && state != STOPPING) {
                     ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
                             true /* ignoreVisibility */);
@@ -1276,31 +1363,25 @@
             // precede the configuration change from the resize.
             mLastReportedPictureInPictureMode = inPictureInPictureMode;
             mLastReportedMultiWindowMode = inPictureInPictureMode;
-            if (targetRootTaskBounds != null && !targetRootTaskBounds.isEmpty()) {
-                computeConfigurationAfterMultiWindowModeChange();
-            }
             ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
                     true /* ignoreVisibility */);
         }
     }
 
-    private void computeConfigurationAfterMultiWindowModeChange() {
-        final Configuration newConfig = new Configuration();
-        newConfig.setTo(task.getRequestedOverrideConfiguration());
-        Rect outBounds = newConfig.windowConfiguration.getBounds();
-        final Configuration parentConfig = task.getParent().getConfiguration();
-        task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
-        task.computeConfigResourceOverrides(newConfig, parentConfig);
-    }
-
     Task getTask() {
         return task;
     }
 
+    @Nullable
+    TaskFragment getTaskFragment() {
+        WindowContainer parent = getParent();
+        return parent != null ? parent.asTaskFragment() : null;
+    }
+
     @Override
     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
-        final Task oldTask = oldParent != null ? (Task) oldParent : null;
-        final Task newTask = newParent != null ? (Task) newParent : null;
+        final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
+        final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
         this.task = newTask;
 
         super.onParentChanged(newParent, oldParent);
@@ -1358,11 +1439,12 @@
 
         updateColorTransform();
 
-        if (oldTask != null) {
-            oldTask.cleanUpActivityReferences(this);
+        if (oldParent != null) {
+            ((TaskFragment) oldParent).cleanUpActivityReferences(this);
         }
-        if (newTask != null && isState(RESUMED)) {
-            newTask.setResumedActivity(this, "onParentChanged");
+
+        if (newParent != null && isState(RESUMED)) {
+            ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
         }
 
         if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -1702,6 +1784,7 @@
                     ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
             mHandoverLaunchDisplayId = options.getLaunchDisplayId();
             mLaunchCookie = options.getLaunchCookie();
+            mLaunchRootTask = options.getLaunchRootTask();
         }
 
         mPersistentState = persistentState;
@@ -1783,6 +1866,13 @@
             task.setRootProcess(proc);
         }
         proc.addActivityIfNeeded(this);
+
+        // Update the associated task fragment after setting the process, since it's required for
+        // filtering to only report activities that belong to the same process.
+        final TaskFragment tf = getTaskFragment();
+        if (tf != null) {
+            tf.sendTaskFragmentInfoChanged();
+        }
     }
 
     boolean hasProcess() {
@@ -1912,6 +2002,7 @@
         }
     }
 
+    @VisibleForTesting
     boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
             IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
@@ -2247,13 +2338,13 @@
     }
 
     void removeStartingWindow() {
+        if (transferSplashScreenIfNeeded()) {
+            return;
+        }
         removeStartingWindowAnimation(true /* prepareAnimation */);
     }
 
     void removeStartingWindowAnimation(boolean prepareAnimation) {
-        if (transferSplashScreenIfNeeded()) {
-            return;
-        }
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
         if (mStartingWindow == null) {
             if (mStartingData != null) {
@@ -2326,23 +2417,24 @@
     }
 
     /**
-     * Reparents this activity into {@param newTask} at the provided {@param position}.  The caller
-     * should ensure that the {@param newTask} is not already the parent of this activity.
+     * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
+     * caller should ensure that the {@param newTaskFrag} is not already the parent of this
+     * activity.
      */
-    void reparent(Task newTask, int position, String reason) {
+    void reparent(TaskFragment newTaskFrag, int position, String reason) {
         if (getParent() == null) {
             Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
             return;
         }
-        final Task prevTask = task;
-        if (prevTask == newTask) {
-            throw new IllegalArgumentException(reason + ": task=" + newTask
+        final TaskFragment prevTaskFrag = getTaskFragment();
+        if (prevTaskFrag == newTaskFrag) {
+            throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag
                     + " is already the parent of r=" + this);
         }
 
         ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
-                + " to task=%d at %d", this, task.mTaskId, position);
-        reparent(newTask, position);
+                + " to new task fragment in task=%d at %d", this, task.mTaskId, position);
+        reparent(newTaskFrag, position);
     }
 
     private boolean isHomeIntent(Intent intent) {
@@ -2908,7 +3000,7 @@
         }
 
         final Task rootTask = getRootTask();
-        final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null)
+        final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null)
                 && rootTask.isFocusedRootTaskOnDisplay()
                 // Do not adjust focus task because the task will be reused to launch new activity.
                 && !task.isClearingToReuseTask();
@@ -2987,12 +3079,12 @@
                 // Tell window manager to prepare for this one to be removed.
                 setVisibility(false);
 
-                if (task.getPausingActivity() == null) {
+                if (getTaskFragment().getPausingActivity() == null) {
                     ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);
                     if (DEBUG_USER_LEAVING) {
                         Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
                     }
-                    task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+                    getTaskFragment().startPausing(false /* userLeaving */, false /* uiSleeping */,
                             null /* resuming */, "finish");
                 }
 
@@ -3123,8 +3215,8 @@
 
         // Clear last paused activity to ensure top activity can be resumed during sleeping.
         if (isNextNotYetVisible && mDisplayContent.isSleeping()
-                && next == next.getRootTask().mLastPausedActivity) {
-            next.getRootTask().mLastPausedActivity = null;
+                && next == next.getTaskFragment().mLastPausedActivity) {
+            next.getTaskFragment().clearLastPausedActivity();
         }
 
         if (isCurrentVisible) {
@@ -3311,8 +3403,8 @@
             if (DEBUG_SWITCH) {
                 final Task task = getTask();
                 Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
-                        + " resumed=" + task.getResumedActivity()
-                        + " pausing=" + task.getPausingActivity()
+                        + " resumed=" + task.getTopResumedActivity()
+                        + " pausing=" + task.getTopPausingActivity()
                         + " for reason " + reason);
             }
             return destroyImmediately(reason);
@@ -3391,7 +3483,7 @@
      * Note: Call before {@link #removeFromHistory(String)}.
      */
     void cleanUp(boolean cleanServices, boolean setState) {
-        task.cleanUpActivityReferences(this);
+        getTaskFragment().cleanUpActivityReferences(this);
         clearLastParentBeforePip();
 
         // Clean up the splash screen if it was still displayed.
@@ -3499,7 +3591,7 @@
             // failed more than twice. Skip activities that's already finishing cleanly by itself.
             remove = false;
         } else if ((!mHaveState && !stateNotNeeded
-                && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) {
+                && !isState(State.RESTARTING_PROCESS)) || finishing) {
             // Don't currently have state for the activity, or it is finishing -- always remove it.
             remove = true;
         } else if (!mVisibleRequested && launchCount > 2
@@ -4223,6 +4315,9 @@
     private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
         final int animationType = pendingOptions.getAnimationType();
         final DisplayContent displayContent = getDisplayContent();
+        AnimationOptions options = null;
+        IRemoteCallback startCallback = null;
+        IRemoteCallback finishCallback = null;
         switch (animationType) {
             case ANIM_CUSTOM:
                 displayContent.mAppTransition.overridePendingAppTransition(
@@ -4232,11 +4327,19 @@
                         pendingOptions.getAnimationStartedListener(),
                         pendingOptions.getAnimationFinishedListener(),
                         pendingOptions.getOverrideTaskTransition());
+                options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
+                        pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+                        pendingOptions.getOverrideTaskTransition());
+                startCallback = pendingOptions.getAnimationStartedListener();
+                finishCallback = pendingOptions.getAnimationFinishedListener();
                 break;
             case ANIM_CLIP_REVEAL:
                 displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
                         pendingOptions.getStartX(), pendingOptions.getStartY(),
                         pendingOptions.getWidth(), pendingOptions.getHeight());
+                options = AnimationOptions.makeClipRevealAnimOptions(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
                 if (intent.getSourceBounds() == null) {
                     intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                             pendingOptions.getStartY(),
@@ -4248,6 +4351,9 @@
                 displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
                         pendingOptions.getStartX(), pendingOptions.getStartY(),
                         pendingOptions.getWidth(), pendingOptions.getHeight());
+                options = AnimationOptions.makeScaleUpAnimOptions(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
                 if (intent.getSourceBounds() == null) {
                     intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                             pendingOptions.getStartY(),
@@ -4263,6 +4369,9 @@
                         pendingOptions.getStartX(), pendingOptions.getStartY(),
                         pendingOptions.getAnimationStartedListener(),
                         scaleUp);
+                options = AnimationOptions.makeThumnbnailAnimOptions(buffer,
+                        pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
+                startCallback = pendingOptions.getAnimationStartedListener();
                 if (intent.getSourceBounds() == null && buffer != null) {
                     intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                             pendingOptions.getStartY(),
@@ -4302,6 +4411,7 @@
             case ANIM_OPEN_CROSS_PROFILE_APPS:
                 displayContent.mAppTransition
                         .overridePendingAppTransitionStartCrossProfileApps();
+                options = AnimationOptions.makeCrossProfileAnimOptions();
                 break;
             case ANIM_NONE:
             case ANIM_UNDEFINED:
@@ -4310,6 +4420,11 @@
                 Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
                 break;
         }
+
+        if (options != null) {
+            mAtmService.getTransitionController().setOverrideAnimation(options,
+                    startCallback, finishCallback);
+        }
     }
 
     void clearAllDrawn() {
@@ -4442,6 +4557,10 @@
         }
     }
 
+    boolean getDeferHidingClient() {
+        return mDeferHidingClient;
+    }
+
     @Override
     boolean isVisible() {
         // If the activity isn't hidden then it is considered visible and there is no need to check
@@ -4606,7 +4725,7 @@
         }
 
         // If in a transition, defer commits for activities that are going invisible
-        if (!visible && mAtmService.getTransitionController().inTransition()) {
+        if (!visible && mAtmService.getTransitionController().inTransition(this)) {
             return;
         }
         // If we are preparing an app transition, then delay changing
@@ -4725,10 +4844,12 @@
      *                this has become invisible.
      */
     private void postApplyAnimation(boolean visible) {
+        final boolean usingShellTransitions =
+                mAtmService.getTransitionController().getTransitionPlayer() != null;
         final boolean delayed = isAnimating(PARENTS | CHILDREN,
                 ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
                         | ANIMATION_TYPE_RECENTS);
-        if (!delayed) {
+        if (!delayed && !usingShellTransitions) {
             // We aren't delayed anything, but exiting windows rely on the animation finished
             // callback being called in case the ActivityRecord was pretending to be delayed,
             // which we might have done because we were in closing/opening apps list.
@@ -4747,8 +4868,8 @@
         // updated.
         // If we're becoming invisible, update the client visibility if we are not running an
         // animation. Otherwise, we'll update client visibility in onAnimationFinished.
-        if (visible || !isAnimating(PARENTS,
-                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+        if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+                || usingShellTransitions) {
             setClientVisible(visible);
         }
 
@@ -4857,7 +4978,7 @@
         return mCurrentLaunchCanTurnScreenOn;
     }
 
-    void setState(ActivityState state, String reason) {
+    void setState(State state, String reason) {
         ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
                 this, getState(), state, reason);
 
@@ -4869,8 +4990,8 @@
 
         mState = state;
 
-        if (task != null) {
-            task.onActivityStateChanged(this, state, reason);
+        if (getTaskFragment() != null) {
+            getTaskFragment().onActivityStateChanged(this, state, reason);
         }
 
         // The WindowManager interprets the app stopping signal as
@@ -4930,44 +5051,42 @@
         }
     }
 
-    ActivityState getState() {
+    State getState() {
         return mState;
     }
 
     /**
      * Returns {@code true} if the Activity is in the specified state.
      */
-    boolean isState(ActivityState state) {
+    boolean isState(State state) {
         return state == mState;
     }
 
     /**
      * Returns {@code true} if the Activity is in one of the specified states.
      */
-    boolean isState(ActivityState state1, ActivityState state2) {
+    boolean isState(State state1, State state2) {
         return state1 == mState || state2 == mState;
     }
 
     /**
      * Returns {@code true} if the Activity is in one of the specified states.
      */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
+    boolean isState(State state1, State state2, State state3) {
         return state1 == mState || state2 == mState || state3 == mState;
     }
 
     /**
      * Returns {@code true} if the Activity is in one of the specified states.
      */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
-            ActivityState state4) {
+    boolean isState(State state1, State state2, State state3, State state4) {
         return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
     }
 
     /**
      * Returns {@code true} if the Activity is in one of the specified states.
      */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
-            ActivityState state4, ActivityState state5) {
+    boolean isState(State state1, State state2, State state3, State state4, State state5) {
         return state1 == mState || state2 == mState || state3 == mState || state4 == mState
                 || state5 == mState;
     }
@@ -4975,8 +5094,8 @@
     /**
      * Returns {@code true} if the Activity is in one of the specified states.
      */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
-            ActivityState state4, ActivityState state5, ActivityState state6) {
+    boolean isState(State state1, State state2, State state3, State state4, State state5,
+            State state6) {
         return state1 == mState || state2 == mState || state3 == mState || state4 == mState
                 || state5 == mState || state6 == mState;
     }
@@ -5168,7 +5287,8 @@
             // returns. Just need to confirm this reasoning makes sense.
             final boolean deferHidingClient = canEnterPictureInPicture
                     && !isState(STARTED, STOPPING, STOPPED, PAUSED);
-            if (deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
+            if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
+                    && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
                 // Go ahead and just put the activity in pip if it supports auto-pip.
                 mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
                 return;
@@ -5184,13 +5304,6 @@
                     supportsEnterPipOnTaskSwitch = false;
                     break;
                 case RESUMED:
-                    // If the app is capable of entering PIP, we should try pausing it now
-                    // so it can PIP correctly.
-                    if (deferHidingClient) {
-                        task.startPausingLocked(false /* uiSleeping */,
-                                null /* resuming */, "makeInvisible");
-                        break;
-                    }
                 case INITIALIZING:
                 case PAUSING:
                 case PAUSED:
@@ -5287,7 +5400,8 @@
      */
     private boolean shouldBeResumed(ActivityRecord activeActivity) {
         return shouldMakeActive(activeActivity) && isFocusable()
-                && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
+                && getTaskFragment().getVisibility(activeActivity)
+                        == TASK_FRAGMENT_VISIBILITY_VISIBLE
                 && canResumeByCompat();
     }
 
@@ -5341,7 +5455,7 @@
         if (!task.hasChild(this)) {
             throw new IllegalStateException("Activity not found in its task");
         }
-        return task.topRunningActivity() == this;
+        return getTaskFragment().topRunningActivity() == this;
     }
 
     void handleAlreadyVisible() {
@@ -5430,16 +5544,17 @@
         ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken,
                 timeout);
 
-        if (task != null) {
+        final TaskFragment taskFragment = getTaskFragment();
+        if (taskFragment != null) {
             removePauseTimeout();
 
-            final ActivityRecord pausingActivity = task.getPausingActivity();
+            final ActivityRecord pausingActivity = taskFragment.getPausingActivity();
             if (pausingActivity == this) {
                 ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this,
                         (timeout ? "(due to timeout)" : " (pause complete)"));
                 mAtmService.deferWindowLayout();
                 try {
-                    task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+                    taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
                 } finally {
                     mAtmService.continueWindowLayout();
                 }
@@ -5854,6 +5969,9 @@
         if (task != null) {
             task.setHasBeenVisible(true);
         }
+        // Clear indicated launch root task because there's no trampoline activity to expect after
+        // the windows are drawn.
+        mLaunchRootTask = null;
     }
 
     /** Called when the windows associated app window container are visible. */
@@ -6102,9 +6220,9 @@
                 return this;
             }
             // Try to use the one which is closest to top.
-            ActivityRecord r = rootTask.getResumedActivity();
+            ActivityRecord r = rootTask.getTopResumedActivity();
             if (r == null) {
-                r = rootTask.getPausingActivity();
+                r = rootTask.getTopPausingActivity();
             }
             if (r != null) {
                 return r;
@@ -6165,6 +6283,12 @@
         return null;
     }
 
+    @Nullable
+    static ActivityRecord isInAnyTask(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        return (r != null && r.isAttached()) ? r : null;
+    }
+
     /**
      * @return display id to which this record is attached,
      *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
@@ -6182,7 +6306,8 @@
             // This would be redundant.
             return false;
         }
-        if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity()
+        if (isState(RESUMED) || getRootTask() == null
+                || this == getTaskFragment().getPausingActivity()
                 || !mHaveState || !stopped) {
             // We're not ready for this kind of thing.
             return false;
@@ -6369,13 +6494,13 @@
         final boolean newSingleActivity = !newTask && !activityCreated
                 && task.getActivity((r) -> !r.finishing && r != this) == null;
 
-        final boolean shown = addStartingWindow(packageName, resolvedTheme,
+        final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null,
                 newTask || newSingleActivity, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
-        if (shown) {
-            mStartingWindowState = STARTING_WINDOW_SHOWN;
+        if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
+            Slog.d(TAG, "Scheduled starting window for " + this);
         }
     }
 
@@ -6387,14 +6512,12 @@
      * It should only be called if this activity is behind other fullscreen activity.
      */
     void cancelInitializing() {
-        if (mStartingWindowState == STARTING_WINDOW_SHOWN) {
+        if (mStartingData != null) {
             // Remove orphaned starting window.
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
-            mStartingWindowState = STARTING_WINDOW_REMOVED;
             removeStartingWindowAnimation(false /* prepareAnimation */);
         }
-        if (isState(INITIALIZING) && !shouldBeVisible(
-                true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
+        if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
             // Remove the unknown visibility record because an invisible activity shouldn't block
             // the keyguard transition.
             mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
@@ -6752,8 +6875,7 @@
         }
         final Configuration displayConfig = mDisplayContent.getConfiguration();
         return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
-                appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
-                displayConfig.orientation);
+                appRect, insets, thumbnailHeader, task, displayConfig.orientation);
     }
 
     @Override
@@ -7178,7 +7300,8 @@
             // If the activity has requested override bounds, the configuration needs to be
             // computed accordingly.
             if (!matchParentBounds()) {
-                task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+                getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
+                        newParentConfiguration);
             }
         // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
         // are already calculated in resolveFixedOrientationConfiguration.
@@ -7293,7 +7416,7 @@
         }
 
         // Since bounds has changed, the configuration needs to be computed accordingly.
-        task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+        getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
     }
 
     /**
@@ -7309,8 +7432,53 @@
     }
 
     /**
-     * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
-     * change and the requested orientation is different from the parent.
+     * In some cases, applying insets to bounds changes the orientation. For example, if a
+     * close-to-square display rotates to portrait to respect a portrait orientation activity, after
+     * insets such as the status and nav bars are applied, the activity may actually have a
+     * landscape orientation. This method checks whether the orientations of the activity window
+     * with and without insets match or if the orientation with insets already matches the
+     * requested orientation. If not, it may be necessary to letterbox the window.
+     * @param parentBounds are the new parent bounds passed down to the activity and should be used
+     *                     to compute the stable bounds.
+     * @param outBounds will store the stable bounds, which are the bounds with insets applied.
+     *                  These should be used to compute letterboxed bounds if orientation is not
+     *                  respected when insets are applied.
+     */
+    private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) {
+        if (mDisplayContent == null) {
+            return true;
+        }
+        // Only need to make changes if activity sets an orientation
+        final int requestedOrientation = getRequestedConfigurationOrientation();
+        if (requestedOrientation == ORIENTATION_UNDEFINED) {
+            return true;
+        }
+        // Compute parent orientation from bounds
+        final int orientation = parentBounds.height() >= parentBounds.width()
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+        // Compute orientation from stable parent bounds (= parent bounds with insets applied)
+        final Task task = getTask();
+        task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
+                outBounds /* outStableBounds */, parentBounds /* bounds */,
+                mDisplayContent.getDisplayInfo());
+        final int orientationWithInsets = outBounds.height() >= outBounds.width()
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+        // If orientation does not match the orientation with insets applied, then a
+        // display rotation will not be enough to respect orientation. However, even if they do
+        // not match but the orientation with insets applied matches the requested orientation, then
+        // there is no need to modify the bounds because when insets are applied, the activity will
+        // have the desired orientation.
+        return orientation == orientationWithInsets
+                || orientationWithInsets == requestedOrientation;
+    }
+
+    /**
+     * Computes bounds (letterbox or pillarbox) when either:
+     * 1. The parent doesn't handle the orientation change and the requested orientation is
+     *    different from the parent (see {@link DisplayContent#setIgnoreOrientationRequest()}.
+     * 2. The parent handling the orientation is not enough. This occurs when the display rotation
+     *    may not be enough to respect orientation requests (see {@link
+     *    ActivityRecord#orientationRespectedWithInsets}).
      *
      * <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
      * in this method.
@@ -7318,9 +7486,14 @@
     private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
             int windowingMode) {
         mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
-        if (handlesOrientationChangeFromDescendant()) {
+        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+        final Rect containerBounds = new Rect(parentBounds);
+        boolean orientationRespectedWithInsets =
+                orientationRespectedWithInsets(parentBounds, containerBounds);
+        if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
             // No need to letterbox because of fixed orientation. Display will handle
-            // fixed-orientation requests.
+            // fixed-orientation requests and a display rotation is enough to respect requested
+            // orientation with insets applied.
             return;
         }
         if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) {
@@ -7340,7 +7513,8 @@
         // If the activity requires a different orientation (either by override or activityInfo),
         // make it fit the available bounds by scaling down its bounds.
         final int forcedOrientation = getRequestedConfigurationOrientation();
-        if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
+        if (forcedOrientation == ORIENTATION_UNDEFINED
+                || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
             return;
         }
 
@@ -7352,67 +7526,83 @@
             return;
         }
 
-        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
-        final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
-        final Rect containingBounds = new Rect();
-        final Rect containingAppBounds = new Rect();
-        // Need to shrink the containing bounds into a square because the parent orientation does
-        // not match the activity requested orientation.
-        if (forcedOrientation == ORIENTATION_LANDSCAPE) {
-            // Shrink height to match width. Position height within app bounds.
-            final int bottom = Math.min(parentAppBounds.top + parentBounds.width(),
-                    parentAppBounds.bottom);
-            containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right,
-                    bottom);
-            containingAppBounds.set(parentAppBounds.left, parentAppBounds.top,
-                    parentAppBounds.right, bottom);
-        } else {
-            // Shrink width to match height. Position width within app bounds.
-            final int right = Math.min(parentAppBounds.left + parentBounds.height(),
-                    parentAppBounds.right);
-            containingBounds.set(parentAppBounds.left, parentBounds.top, right,
-                    parentBounds.bottom);
-            containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right,
-                    parentAppBounds.bottom);
-        }
-
-        Rect mTmpFullBounds = new Rect(resolvedBounds);
-        resolvedBounds.set(containingBounds);
+        // TODO(b/182268157) merge aspect ratio logic here and in
+        // {@link ActivityRecord#applyAspectRatio}
+        // if no aspect ratio constraints are provided, parent aspect ratio is used
+        float aspectRatio = computeAspectRatio(parentBounds);
 
         // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
         // set-fixed-orientation-letterbox-aspect-ratio.
         final float letterboxAspectRatioOverride =
                 mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
-        final float desiredAspectRatio =
-                letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
-                        ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
-        // Apply aspect ratio to resolved bounds
-        mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds,
-                containingBounds, desiredAspectRatio, true);
+        aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+                ? letterboxAspectRatioOverride : aspectRatio;
 
-        // Vertically center if orientation is landscape. Bounds will later be horizontally centered
-        // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
+        // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
+        // order to use the extra available space.
+        final float maxAspectRatio = info.getMaxAspectRatio();
+        final float minAspectRatio = info.getMinAspectRatio();
+        if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) {
+            aspectRatio = maxAspectRatio;
+        } else if (aspectRatio < minAspectRatio) {
+            aspectRatio = minAspectRatio;
+        }
+
+        // Store the current bounds to be able to revert to size compat mode values below if needed.
+        final Rect prevResolvedBounds = new Rect(resolvedBounds);
+
+        // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored
+        // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()},
+        // to ensure that aspect ratio is respected after insets are applied.
+        int activityWidth;
+        int activityHeight;
         if (forcedOrientation == ORIENTATION_LANDSCAPE) {
-            final int offsetY = parentBounds.centerY() - resolvedBounds.centerY();
-            resolvedBounds.offset(0, offsetY);
+            activityWidth = parentBounds.width();
+            // Compute height from stable bounds width to ensure orientation respected after insets.
+            activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio);
+            // Landscape is defined as width > height. To ensure activity is landscape when aspect
+            // ratio is close to 1, reduce the height by one pixel.
+            if (activityWidth == activityHeight) {
+                activityHeight -= 1;
+            }
+            // Center vertically within stable bounds in landscape to ensure insets do not trim
+            // height.
+            final int top = containerBounds.centerY() - activityHeight / 2;
+            resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight);
+        } else {
+            activityHeight = parentBounds.height();
+            // Compute width from stable bounds height to ensure orientation respected after insets.
+            activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio);
+            // Center horizontally in portrait. For now, align to left and allow
+            // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center
+            // horizontally. Exclude left insets from parent to ensure cutout does not trim width.
+            final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+            resolvedBounds.set(parentAppBounds.left, parentBounds.top,
+                    parentAppBounds.left + activityWidth, parentBounds.bottom);
         }
 
         if (mCompatDisplayInsets != null) {
             mCompatDisplayInsets.getBoundsByRotation(
                     mTmpBounds, newParentConfig.windowConfiguration.getRotation());
-            if (resolvedBounds.width() != mTmpBounds.width()
-                    || resolvedBounds.height() != mTmpBounds.height()) {
+            // Insets may differ between different rotations, for example in the case of a display
+            // cutout. To ensure consistent bounds across rotations, compare the activity dimensions
+            // minus insets from the rotation the compat bounds were computed in.
+            Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds,
+                    mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]);
+            if (activityWidth != mTmpBounds.width()
+                    || activityHeight != mTmpBounds.height()) {
                 // The app shouldn't be resized, we only do fixed orientation letterboxing if the
                 // compat bounds are also from the same fixed orientation letterbox. Otherwise,
                 // clear the fixed orientation bounds to show app in size compat mode.
-                resolvedBounds.set(mTmpFullBounds);
+                resolvedBounds.set(prevResolvedBounds);
                 return;
             }
         }
 
         // Calculate app bounds using fixed orientation bounds because they will be needed later
         // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
-        task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+        getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
+                newParentConfig);
         mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
     }
 
@@ -7436,11 +7626,13 @@
         // then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
         if (!mTmpBounds.isEmpty()) {
             resolvedBounds.set(mTmpBounds);
+            // Exclude the horizontal decor area.
+            resolvedBounds.left = parentAppBounds.left;
         }
         if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
             // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
             // restrict, the bounds should be the requested override bounds.
-            task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+            getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                     getFixedRotationTransformDisplayInfo());
         }
     }
@@ -7496,14 +7688,21 @@
             mIsAspectRatioApplied =
                     applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
         }
+        // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
+        // the container app bounds. Otherwise the entire container bounds are available.
+        final boolean fillContainer = resolvedBounds.equals(containingBounds);
+        if (!fillContainer) {
+            // The horizontal position should not cover insets.
+            resolvedBounds.left = containingAppBounds.left;
+        }
 
         // Use resolvedBounds to compute other override configurations such as appBounds. The bounds
         // are calculated in compat container space. The actual position on screen will be applied
         // later, so the calculation is simpler that doesn't need to involve offset from parent.
-        task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+        getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                 mCompatDisplayInsets);
         // Use current screen layout as source because the size of app is independent to parent.
-        resolvedConfig.screenLayout = Task.computeScreenLayoutOverride(
+        resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
                 getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
                 resolvedConfig.screenHeightDp);
 
@@ -7562,7 +7761,6 @@
         // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
         // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
-        final boolean fillContainer = resolvedBounds.equals(containingBounds);
         final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
         final int screenPosY = containerBounds.top;
         if (screenPosX != 0 || screenPosY != 0) {
@@ -7647,6 +7845,10 @@
         if (getUid() == SYSTEM_UID) {
             return false;
         }
+        // Do not sandbox to activity window bounds if the feature is disabled.
+        if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) {
+            return false;
+        }
         // Never apply sandboxing to an app that should be explicitly excluded from the config.
         if (info != null && info.neverSandboxDisplayApis()) {
             return false;
@@ -7800,12 +8002,6 @@
         return true;
     }
 
-    private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
-            Rect containingBounds) {
-        return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
-                0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
-    }
-
     /**
      * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
      * made to outBounds.
@@ -7814,19 +8010,17 @@
      */
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
-            Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
+            Rect containingBounds) {
         final float maxAspectRatio = info.getMaxAspectRatio();
         final Task rootTask = getRootTask();
         final float minAspectRatio = info.getMinAspectRatio();
 
         if (task == null || rootTask == null
-                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
-                    && !fixedOrientationLetterboxed)
-                || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
+                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
+                || (maxAspectRatio == 0 && minAspectRatio == 0)
                 || isInVrUiMode(getConfiguration())) {
-            // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
-            // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
-            // are in VR mode.
+            // We don't enforce aspect ratio if the activity task is in multiwindow unless it
+            // is in size-compat mode. We also don't set it if we are in VR mode.
             return false;
         }
 
@@ -7834,30 +8028,20 @@
         final int containingAppHeight = containingAppBounds.height();
         final float containingRatio = computeAspectRatio(containingAppBounds);
 
-        if (desiredAspectRatio < 1) {
-            desiredAspectRatio = containingRatio;
-        }
-
-        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
-            desiredAspectRatio = maxAspectRatio;
-        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
-            desiredAspectRatio = minAspectRatio;
-        }
-
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio > desiredAspectRatio) {
+        if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
-                activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
             } else {
                 // Height is the shorter side, so we use that to figure-out what the max. width
                 // should be given the aspect ratio.
-                activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < desiredAspectRatio) {
+        } else if (containingRatio < minAspectRatio) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
@@ -7885,9 +8069,9 @@
                     break;
             }
             if (adjustWidth) {
-                activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
             } else {
-                activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
             }
         }
 
@@ -7911,13 +8095,6 @@
         }
         outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
 
-        // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
-        // container app bounds. Otherwise the entire container bounds are available.
-        if (!outBounds.equals(containingBounds)) {
-            // The horizontal position should not cover insets.
-            outBounds.left = containingAppBounds.left;
-        }
-
         return true;
     }
 
@@ -8070,7 +8247,9 @@
         if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
             configChangeFlags |= changes;
-            startFreezingScreenLocked(globalChanges);
+            if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+                startFreezingScreenLocked(globalChanges);
+            }
             forceNewConfig = false;
             preserveWindow &= isResizeOnlyChange(changes);
             final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
@@ -8673,6 +8852,8 @@
      * compatibility mode activity compute the configuration without relying on its current display.
      */
     static class CompatDisplayInsets {
+        /** The original rotation the compat insets were computed in */
+        final @Rotation int mOriginalRotation;
         /** The container width on rotation 0. */
         private final int mWidth;
         /** The container height on rotation 0. */
@@ -8699,6 +8880,7 @@
         /** Constructs the environment to simulate the bounds behavior of the given container. */
         CompatDisplayInsets(DisplayContent display, ActivityRecord container,
                 @Nullable Rect fixedOrientationBounds) {
+            mOriginalRotation = display.getRotation();
             mIsFloating = container.getWindowConfiguration().tasksAreFloating();
             if (mIsFloating) {
                 final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8845,7 +9027,8 @@
                 outAppBounds.offset(insets.left, insets.top);
             } else if (rotation != ROTATION_UNDEFINED) {
                 // Ensure the app bounds won't overlap with insets.
-                Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
+                TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
+                        mNonDecorInsets[rotation]);
             }
         }
     }
@@ -8914,6 +9097,14 @@
         return false;
     }
 
+    @Override
+    void finishSync(Transaction outMergedTransaction, boolean cancel) {
+        // This override is just for getting metrics. allFinished needs to be checked before
+        // finish because finish resets all the states.
+        mLastAllReadyAtSync = allSyncFinished();
+        super.finishSync(outMergedTransaction, cancel);
+    }
+
     static class Builder {
         private final ActivityTaskManagerService mAtmService;
         private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 8540fa7..30c7b23 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -16,10 +16,10 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
 
 import android.util.ArraySet;
 import android.util.Slog;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b6f2f24..c3cd61a 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -27,6 +27,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -38,6 +39,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -488,6 +490,23 @@
         return START_SUCCESS;
     }
 
+    /**
+     * Starts an activity in the TaskFragment.
+     * @param taskFragment TaskFragment {@link TaskFragment} to start the activity in.
+     * @param activityIntent intent to start the activity.
+     * @param activityOptions ActivityOptions to start the activity with.
+     * @return the start result.
+     */
+    int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
+            @NonNull Intent activityIntent, @Nullable Bundle activityOptions) {
+        return obtainStarter(activityIntent, "startActivityInTaskFragment")
+                .setActivityOptions(activityOptions)
+                .setInTaskFragment(taskFragment)
+                .setCallingUid(Binder.getCallingUid())
+                .setCallingPid(Binder.getCallingPid())
+                .execute();
+    }
+
     void registerRemoteAnimationForNextActivityStart(String packageName,
             RemoteAnimationAdapter adapter) {
         mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9a25fc..c881359 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.ACTIVITY_EMBEDDING;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
@@ -28,6 +29,7 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 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;
@@ -58,6 +60,7 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -74,7 +77,6 @@
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -177,6 +179,7 @@
     private int mPreferredWindowingMode;
 
     private Task mInTask;
+    private TaskFragment mInTaskFragment;
     @VisibleForTesting
     boolean mAddingToTask;
     private Task mReuseTask;
@@ -342,6 +345,7 @@
         boolean avoidMoveToFront;
         ActivityRecord[] outActivity;
         Task inTask;
+        TaskFragment inTaskFragment;
         String reason;
         ProfilerInfo profilerInfo;
         Configuration globalConfig;
@@ -392,6 +396,7 @@
             componentSpecified = false;
             outActivity = null;
             inTask = null;
+            inTaskFragment = null;
             reason = null;
             profilerInfo = null;
             globalConfig = null;
@@ -407,7 +412,7 @@
         /**
          * Adopts all values from passed in request.
          */
-        void set(Request request) {
+        void set(@NonNull Request request) {
             caller = request.caller;
             intent = request.intent;
             intentGrants = request.intentGrants;
@@ -432,6 +437,7 @@
             componentSpecified = request.componentSpecified;
             outActivity = request.outActivity;
             inTask = request.inTask;
+            inTaskFragment = request.inTaskFragment;
             reason = request.reason;
             profilerInfo = request.profilerInfo;
             globalConfig = request.globalConfig;
@@ -574,6 +580,7 @@
         mPreferredWindowingMode = starter.mPreferredWindowingMode;
 
         mInTask = starter.mInTask;
+        mInTaskFragment = starter.mInTaskFragment;
         mAddingToTask = starter.mAddingToTask;
         mReuseTask = starter.mReuseTask;
 
@@ -835,6 +842,7 @@
         final int startFlags = request.startFlags;
         final SafeActivityOptions options = request.activityOptions;
         Task inTask = request.inTask;
+        TaskFragment inTaskFragment = request.inTaskFragment;
 
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -864,7 +872,7 @@
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
-            sourceRecord = mRootWindowContainer.isInAnyTask(resultTo);
+            sourceRecord = ActivityRecord.isInAnyTask(resultTo);
             if (DEBUG_RESULTS) {
                 Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);
             }
@@ -1175,8 +1183,8 @@
         }
 
         mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
-                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
-                restrictedBgActivity, intentGrants);
+                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
+                inTask, inTaskFragment, restrictedBgActivity, intentGrants);
 
         if (request.outActivity != null) {
             request.outActivity[0] = mLastStartActivityRecord;
@@ -1546,10 +1554,12 @@
      * Here also ensures that the starting activity is removed if the start wasn't successful.
      */
     private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
-                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-                int startFlags, boolean doResume, ActivityOptions options, Task inTask,
-                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
+            TaskFragment inTaskFragment, boolean restrictedBgActivity,
+            NeededUriGrants intentGrants) {
         int result = START_CANCELED;
+        boolean startResultSuccessful = false;
         final Task startedActivityRootTask;
 
         // Create a transition now to record the original intent of actions taken within
@@ -1564,11 +1574,26 @@
             newTransition.setRemoteTransition(remoteTransition);
         }
         mService.getTransitionController().collect(r);
+        // TODO(b/188669821): Remove when navbar reparenting moves to shell
+        if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
+                && r.getOptions().getTransientLaunch()) {
+            mService.getTransitionController().setIsLegacyRecents();
+        }
         try {
             mService.deferWindowLayout();
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
             result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
+                    startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
+                    intentGrants);
+            startResultSuccessful = ActivityManager.isStartResultSuccessful(result);
+            final boolean taskAlwaysOnTop = options != null && options.getTaskAlwaysOnTop();
+            // Apply setAlwaysOnTop when starting an Activity is successful regardless of creating
+            // a new Activity or recycling the existing Activity.
+            if (taskAlwaysOnTop && startResultSuccessful) {
+                final Task targetRootTask =
+                        mTargetRootTask != null ? mTargetRootTask : mTargetTask.getRootTask();
+                targetRootTask.setAlwaysOnTop(true);
+            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             startedActivityRootTask = handleStartResult(r, result);
@@ -1576,7 +1601,7 @@
             mSupervisor.mUserLeaving = false;
 
             // Transition housekeeping
-            if (!ActivityManager.isStartResultSuccessful(result)) {
+            if (!startResultSuccessful) {
                 if (newTransition != null) {
                     newTransition.abort();
                 }
@@ -1595,7 +1620,8 @@
                         statusBar.collapsePanels();
                     }
                 }
-                if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
+                final boolean started = result == START_SUCCESS || result == START_TASK_TO_FRONT;
+                if (started) {
                     // The activity is started new rather than just brought forward, so record
                     // it as an existence change.
                     mService.getTransitionController().collectExistenceChange(r);
@@ -1603,9 +1629,9 @@
                 if (newTransition != null) {
                     mService.getTransitionController().requestStartTransition(newTransition,
                             mTargetTask, remoteTransition);
-                } else {
+                } else if (started) {
                     // Make the collecting transition wait until this request is ready.
-                    mService.getTransitionController().setReady(false);
+                    mService.getTransitionController().setReady(r, false);
                 }
             }
         }
@@ -1665,15 +1691,15 @@
      *
      * Note: This method should only be called from {@link #startActivityUnchecked}.
      */
-
     // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
     @VisibleForTesting
     int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, Task inTask,
-            boolean restrictedBgActivity, NeededUriGrants intentGrants) {
-        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
-                voiceInteractor, restrictedBgActivity);
+            TaskFragment inTaskFragment, boolean restrictedBgActivity,
+            NeededUriGrants intentGrants) {
+        setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord,
+                voiceSession, voiceInteractor, restrictedBgActivity);
 
         computeLaunchingTaskFlags();
 
@@ -1739,11 +1765,6 @@
 
         if (!mAvoidMoveToFront && mDoResume) {
             mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
-            if (mOptions != null) {
-                if (mOptions.getTaskAlwaysOnTop()) {
-                    mTargetRootTask.setAlwaysOnTop(true);
-                }
-            }
             if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.mInternal.isDreaming()) {
                 // Launching underneath dream activity (fullscreen, always-on-top). Run the launch-
                 // -behind transition so the Activity gets created and starts in visible state.
@@ -1772,7 +1793,7 @@
         mStartActivity.logStartActivity(
                 EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
 
-        mTargetRootTask.mLastPausedActivity = null;
+        mStartActivity.getTaskFragment().clearLastPausedActivity();
 
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
@@ -2023,7 +2044,7 @@
         // We didn't do anything...  but it was needed (a.k.a., client don't use that intent!)
         // And for paranoia, make sure we have correctly resumed the top activity.
         resumeTargetRootTaskIfNeeded();
-      
+
         mLastStartActivityRecord = targetTaskTop;
         return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
     }
@@ -2049,7 +2070,7 @@
         }
 
         // For paranoia, make sure we have correctly resumed the top activity.
-        topRootTask.mLastPausedActivity = null;
+        top.getTaskFragment().clearLastPausedActivity();
         if (mDoResume) {
             mRootWindowContainer.resumeFocusedTasksTopActivities();
         }
@@ -2145,7 +2166,7 @@
                 task.moveActivityToFrontLocked(act);
                 act.updateOptionsLocked(mOptions);
                 deliverNewIntent(act, intentGrants);
-                mTargetRootTask.mLastPausedActivity = null;
+                act.getTaskFragment().clearLastPausedActivity();
             } else {
                 mAddingToTask = true;
             }
@@ -2213,6 +2234,7 @@
         mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
 
         mInTask = null;
+        mInTaskFragment = null;
         mAddingToTask = false;
         mReuseTask = null;
 
@@ -2240,9 +2262,9 @@
     }
 
     private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
-            boolean doResume, int startFlags, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            boolean restrictedBgActivity) {
+            TaskFragment inTaskFragment, boolean doResume, int startFlags,
+            ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
+            IVoiceInteractor voiceInteractor, boolean restrictedBgActivity) {
         reset(false /* clearRequest */);
 
         mStartActivity = r;
@@ -2345,6 +2367,7 @@
             Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
             mInTask = null;
         }
+        mInTaskFragment = inTaskFragment;
 
         mStartFlags = startFlags;
         // If the onlyIfNeeded flag is set, then we can do this if the activity being launched
@@ -2568,13 +2591,28 @@
     /**
      * Figure out which task and activity to bring to front when we have found an existing matching
      * activity record in history. May also clear the task if needed.
+     *
      * @param intentActivity Existing matching activity.
      * @return {@link ActivityRecord} brought to front.
      */
     private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) {
-        mTargetRootTask = intentActivity.getRootTask();
-        mTargetRootTask.mLastPausedActivity = null;
+        intentActivity.getTaskFragment().clearLastPausedActivity();
         Task intentTask = intentActivity.getTask();
+
+        // Only update the target-root-task when it is not indicated.
+        if (mTargetRootTask == null) {
+            if (mSourceRecord != null && mSourceRecord.mLaunchRootTask != null) {
+                // Inherit the target-root-task from source to ensure trampoline activities will be
+                // launched into the same root task.
+                mTargetRootTask = Task.fromWindowContainerToken(mSourceRecord.mLaunchRootTask);
+            } else {
+                final Task launchRootTask =
+                        getLaunchRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions);
+                mTargetRootTask =
+                        launchRootTask != null ? launchRootTask : intentActivity.getRootTask();
+            }
+        }
+
         // If the target task is not in the front, then we need to bring it to the front...
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
         // the same behavior as if a new instance was being started, which means not bringing it
@@ -2602,9 +2640,7 @@
                     intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
                 }
 
-                final Task launchRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags,
-                        intentTask, mOptions);
-                if (launchRootTask == null || launchRootTask == mTargetRootTask) {
+                if (mTargetRootTask == intentActivity.getRootTask()) {
                     // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
                     //  tasks hierarchies.
                     if (mTargetRootTask != intentTask
@@ -2628,7 +2664,7 @@
                             "bringingFoundTaskToFront");
                     mMovedToFront = !wasTopOfVisibleRootTask;
                 } else {
-                    intentTask.reparent(launchRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+                    intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
                             ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
                     mMovedToFront = true;
                 }
@@ -2700,11 +2736,53 @@
         mIntentDelivered = true;
     }
 
-    private void addOrReparentStartingActivity(Task parent, String reason) {
-        if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
-            parent.addChild(mStartActivity);
+    /**
+     * Return {@code true} if the {@param task} has child {@code TaskFragment}s and the
+     * {@param activity} can be embedded in. Otherwise, return {@code false}
+     */
+    private boolean canActivityBeEmbedded(@NonNull ActivityRecord activity, @NonNull Task task) {
+        if (task.isLeafTaskFragment()) {
+            return false;
+        }
+
+        final int taskUid = task.effectiveUid;
+        // Allowing the activity be embedded into leaf TaskFragment if the task is owned by system.
+        if (taskUid == Process.SYSTEM_UID) {
+            return true;
+        }
+
+        // Allowing embedding if the task is owned by an app that has the ACTIVITY_EMBEDDING
+        // permission
+        if (mService.checkPermission(ACTIVITY_EMBEDDING, -1, taskUid) == PERMISSION_GRANTED) {
+            return true;
+        }
+
+        // Allowing embedding if the activity is from the same app that owned the task
+        return activity.isUid(taskUid);
+    }
+
+    private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
+        TaskFragment newParent = task;
+        if (mInTaskFragment != null) {
+            // mInTaskFragment is created and added to the leaf task by task fragment organizer's
+            // request. If the task was resolved and different than mInTaskFragment, reparent the
+            // task to mInTaskFragment for embedding.
+            if (mInTaskFragment.getTask() != task) {
+                task.reparent(mInTaskFragment, POSITION_TOP);
+            } else {
+                newParent = mInTaskFragment;
+            }
+        } else if (canActivityBeEmbedded(mStartActivity, task)) {
+            // Use the child TaskFragment (if any) as the new parent if the activity can be embedded
+            final ActivityRecord top = task.topRunningActivity();
+            newParent = top != null ? top.getTaskFragment() : task;
+        }
+
+        if (mStartActivity.getTaskFragment() == null
+                || mStartActivity.getTaskFragment() == newParent) {
+            newParent.addChild(mStartActivity, POSITION_TOP);
         } else {
-            mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
+            mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
         }
     }
 
@@ -2936,6 +3014,11 @@
         return this;
     }
 
+    ActivityStarter setInTaskFragment(TaskFragment taskFragment) {
+        mRequest.inTaskFragment = taskFragment;
+        return this;
+    }
+
     ActivityStarter setWaitResult(WaitResult result) {
         mRequest.waitResult = result;
         return this;
@@ -3019,5 +3102,7 @@
         pw.print(mDoResume);
         pw.print(" mAddingToTask=");
         pw.println(mAddingToTask);
+        pw.print(" mInTaskFragment=");
+        pw.println(mInTaskFragment);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f3ba56a..48a6626 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -253,7 +253,6 @@
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
 import com.android.server.firewall.IntentFirewall;
-import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -732,6 +731,7 @@
 
     WindowOrganizerController mWindowOrganizerController;
     TaskOrganizerController mTaskOrganizerController;
+    TaskFragmentOrganizerController mTaskFragmentOrganizerController;
 
     @Nullable
     private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
@@ -805,6 +805,8 @@
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
         mWindowOrganizerController = new WindowOrganizerController(this);
         mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
+        mTaskFragmentOrganizerController =
+                mWindowOrganizerController.mTaskFragmentOrganizerController;
     }
 
     public void onSystemReady() {
@@ -1222,8 +1224,8 @@
             // If this is coming from the currently resumed activity, it is
             // effectively saying that app switches are allowed at this point.
             final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
-            if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null
-                    && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid
+            if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
+                    && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
                     == Binder.getCallingUid()) {
                 mAppSwitchesAllowed = true;
             }
@@ -1537,7 +1539,7 @@
                 sourceToken = resultTo;
             }
 
-            sourceRecord = mRootWindowContainer.isInAnyTask(sourceToken);
+            sourceRecord = ActivityRecord.isInAnyTask(sourceToken);
             if (sourceRecord == null) {
                 throw new SecurityException("Called with bad activity token: " + sourceToken);
             }
@@ -1881,25 +1883,42 @@
     @Override
     public void setFocusedTask(int taskId) {
         enforceTaskPermission("setFocusedTask()");
-        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final Task task = mRootWindowContainer.anyTaskForId(taskId,
-                        MATCH_ATTACHED_TASK_ONLY);
-                if (task == null) {
-                    return;
-                }
-                final ActivityRecord r = task.topRunningActivityLocked();
-                if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
-                    mRootWindowContainer.resumeFocusedTasksTopActivities();
-                }
+                setFocusedTask(taskId, null /* touchedActivity */);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
     }
 
+    void setFocusedTask(int taskId, ActivityRecord touchedActivity) {
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId,
+                touchedActivity);
+        final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY);
+        if (task == null) {
+            return;
+        }
+        final ActivityRecord r = task.topRunningActivityLocked();
+        if (r == null) {
+            return;
+        }
+
+        if (r.moveFocusableActivityToTop("setFocusedTask")) {
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
+        } else if (touchedActivity != null && touchedActivity != r
+                && touchedActivity.getTask() == r.getTask()
+                && touchedActivity.getTaskFragment() != r.getTaskFragment()) {
+            // Set the focused app directly since the focused window is not on the
+            // top-most TaskFragment of the top-most Task
+            final DisplayContent displayContent = touchedActivity.getDisplayContent();
+            displayContent.setFocusedApp(touchedActivity);
+            mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                    true /* updateInputWindows */);
+        }
+    }
+
     @Override
     public boolean removeTask(int taskId) {
         mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
@@ -3425,7 +3444,6 @@
 
     @Override
     public IWindowOrganizerController getWindowOrganizerController() {
-        enforceTaskPermission("getWindowOrganizerController()");
         return mWindowOrganizerController;
     }
 
@@ -3769,6 +3787,20 @@
         }
     }
 
+    @Override
+    public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "detachNavigationBarFromApp");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                getTransitionController().legacyDetachNavigationBarFromApp(transition);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     void dumpLastANRLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
         if (mLastANRState == null) {
@@ -5036,11 +5068,6 @@
      * @param imeContainer The DisplayArea that contains the IME window.
      */
     void onImeWindowSetOnDisplayArea(final int pid, @NonNull final DisplayArea imeContainer) {
-        // Don't update process-level configuration for Multi-Client IME process since other
-        // IMEs on other displays will also receive this configuration change due to IME
-        // services use the same application config/context.
-        if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
-
         if (pid == MY_PID || pid < 0) {
             ProtoLog.w(WM_DEBUG_CONFIGURATION,
                     "Trying to update display configuration for system/invalid process.");
@@ -5055,6 +5082,34 @@
         process.registerDisplayAreaConfigurationListener(imeContainer);
     }
 
+    @Override
+    public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "setRunningRemoteTransition");
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            // Also only allow a process which is already runningRemoteAnimation to mark another
+            // process.
+            final WindowProcessController callingProc = getProcessController(callingPid,
+                    callingUid);
+            if (callingProc == null || !callingProc.isRunningRemoteTransition()) {
+                final String msg = "Can't call setRunningRemoteTransition from a process (pid="
+                        + callingPid + " uid=" + callingUid + ") which isn't itself running a "
+                        + "remote transition.";
+                Slog.e(TAG, msg);
+                throw new SecurityException(msg);
+            }
+            final WindowProcessController wpc = getProcessController(caller);
+            if (wpc == null) {
+                Slog.w(TAG, "Unable to find process for application " + caller);
+                return;
+            }
+            wpc.setRunningRemoteAnimation(true /* running */);
+            callingProc.addRemoteAnimationDelegate(wpc);
+        }
+    }
+
     final class H extends Handler {
         static final int REPORT_TIME_TRACKER_MSG = 1;
         static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e3459a1..f728a48 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -49,6 +49,9 @@
 
 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.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
@@ -73,8 +76,6 @@
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
 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.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
 import static com.android.server.wm.Task.TAG_CLEANUP;
@@ -140,7 +141,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.TransferPipe;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
@@ -151,7 +151,6 @@
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -1377,7 +1376,8 @@
             }
 
             mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
-                    0 /* flags */, task, options != null ? options.getRemoteTransition() : null);
+                    0 /* flags */, task, task /* readyGroupRef */,
+                    options != null ? options.getRemoteTransition() : null);
             reason = reason + " findTaskToMoveToFront";
             boolean reparented = false;
             if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -1991,76 +1991,14 @@
     static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
             String prefix, String label, boolean complete, boolean brief, boolean client,
             String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
-        String innerPrefix = null;
-        String[] args = null;
         boolean printed = false;
-        for (int i=list.size()-1; i>=0; i--) {
+        for (int i = list.size() - 1; i >= 0; i--) {
             final ActivityRecord r = list.get(i);
-            if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
-                continue;
-            }
-            if (innerPrefix == null) {
-                innerPrefix = prefix + "      ";
-                args = new String[0];
-            }
-            printed = true;
-            final boolean full = !brief && (complete || !r.isInHistory());
-            if (needNL) {
-                pw.println("");
-                needNL = false;
-            }
-            if (header != null) {
-                header.run();
-                header = null;
-            }
-            if (lastTask != r.getTask()) {
-                lastTask = r.getTask();
-                pw.print(prefix);
-                pw.print(full ? "* " : "  ");
-                pw.println(lastTask);
-                if (full) {
-                    lastTask.dump(pw, prefix + "  ");
-                } else if (complete) {
-                    // Complete + brief == give a summary.  Isn't that obvious?!?
-                    if (lastTask.intent != null) {
-                        pw.print(prefix); pw.print("  ");
-                                pw.println(lastTask.intent.toInsecureString());
-                    }
-                }
-            }
-            pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
-            pw.print(" #"); pw.print(i); pw.print(": ");
-            pw.println(r);
-            if (full) {
-                r.dump(pw, innerPrefix, true /* dumpAll */);
-            } else if (complete) {
-                // Complete + brief == give a summary.  Isn't that obvious?!?
-                pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
-                if (r.app != null) {
-                    pw.print(innerPrefix); pw.println(r.app);
-                }
-            }
-            if (client && r.attachedToProcess()) {
-                // flush anything that is already in the PrintWriter since the thread is going
-                // to write to the file descriptor directly
-                pw.flush();
-                try {
-                    TransferPipe tp = new TransferPipe();
-                    try {
-                        r.app.getThread().dumpActivity(
-                                tp.getWriteFd(), r.appToken, innerPrefix, args);
-                        // Short timeout, since blocking here can deadlock with the application.
-                        tp.go(fd, 2000);
-                    } finally {
-                        tp.kill();
-                    }
-                } catch (IOException e) {
-                    pw.println(innerPrefix + "Failure while dumping the activity: " + e);
-                } catch (RemoteException e) {
-                    pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
-                }
-                needNL = true;
-            }
+            ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief,
+                    client, dumpPackage, needNL, header, lastTask);
+            lastTask = r.getTask();
+            header = null;
+            needNL = client && r.attachedToProcess();
         }
         return printed;
     }
@@ -2087,7 +2025,7 @@
     void updateTopResumedActivityIfNeeded() {
         final ActivityRecord prevTopActivity = mTopResumedActivity;
         final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
-        if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) {
+        if (topRootTask == null || topRootTask.getTopResumedActivity() == 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.
@@ -2109,7 +2047,7 @@
         }
 
         // Update the current top activity.
-        mTopResumedActivity = topRootTask.getResumedActivity();
+        mTopResumedActivity = topRootTask.getTopResumedActivity();
         scheduleTopResumedActivityStateIfNeeded();
 
         mService.updateTopApp(mTopResumedActivity);
@@ -2416,8 +2354,7 @@
                     String processName = null;
                     int uid = 0;
                     synchronized (mService.mGlobalLock) {
-                        if (r.attachedToProcess()
-                                && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+                        if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
                             processName = r.app.mName;
                             uid = r.app.mUid;
                         }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1b287f..174b396 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -78,10 +78,7 @@
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
 import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -102,12 +99,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Picture;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.hardware.HardwareBuffer;
 import android.os.Binder;
 import android.os.Debug;
@@ -132,7 +124,6 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.view.animation.ScaleAnimation;
@@ -222,6 +213,7 @@
     private int mNextAppTransitionEnter;
     private int mNextAppTransitionExit;
     private int mNextAppTransitionInPlace;
+    private boolean mNextAppTransitionIsSync;
 
     // Keyed by WindowContainer hashCode.
     private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
@@ -357,6 +349,13 @@
         fetchAppTransitionSpecsFromFuture();
     }
 
+    void abort() {
+        if (mRemoteAnimationController != null) {
+            mRemoteAnimationController.cancelAnimation("aborted");
+        }
+        clear();
+    }
+
     boolean isRunning() {
         return mAppTransitionState == APP_STATE_RUNNING;
     }
@@ -475,6 +474,7 @@
         mNextAppTransitionAnimationsSpecsFuture = null;
         mDefaultNextAppTransitionAnimationSpec = null;
         mAnimationFinishedCallback = null;
+        mNextAppTransitionIsSync = false;
     }
 
     void freeze() {
@@ -641,24 +641,6 @@
     /**
      * Prepares the specified animation with a standard duration, interpolator, etc.
      */
-    Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
-            int appHeight, long duration, Interpolator interpolator) {
-        if (a != null) {
-            if (duration > 0) {
-                a.setDuration(duration);
-            }
-            a.setFillAfter(true);
-            if (interpolator != null) {
-                a.setInterpolator(interpolator);
-            }
-            a.initialize(appWidth, appHeight, appWidth, appHeight);
-        }
-        return a;
-    }
-
-    /**
-     * Prepares the specified animation with a standard duration, interpolator, etc.
-     */
     Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
         // Pick the desired duration.  If this is an inter-activity transition,
         // it  is the standard duration for that.  Otherwise we use the longer
@@ -678,56 +660,16 @@
     }
 
     /**
-     * Return the current thumbnail transition state.
-     */
-    int getThumbnailTransitionState(boolean enter) {
-        if (enter) {
-            if (mNextAppTransitionScaleUp) {
-                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
-            } else {
-                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
-            }
-        } else {
-            if (mNextAppTransitionScaleUp) {
-                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
-            } else {
-                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
-            }
-        }
-    }
-
-    /**
      * Creates an overlay with a background color and a thumbnail for the cross profile apps
      * animation.
      */
     HardwareBuffer createCrossProfileAppsThumbnail(
             @DrawableRes int thumbnailDrawableRes, Rect frame) {
-        final int width = frame.width();
-        final int height = frame.height();
-
-        final Picture picture = new Picture();
-        final Canvas canvas = picture.beginRecording(width, height);
-        canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
-        final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
-        final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
-        drawable.setBounds(
-                (width - thumbnailSize) / 2,
-                (height - thumbnailSize) / 2,
-                (width + thumbnailSize) / 2,
-                (height + thumbnailSize) / 2);
-        drawable.setTint(mContext.getColor(android.R.color.white));
-        drawable.draw(canvas);
-        picture.endRecording();
-
-        return Bitmap.createBitmap(picture).getHardwareBuffer();
+        return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
     }
 
     Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
-        final Animation animation =
-                mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
-        return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
-                appRect.height(), 0, null);
+        return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect);
     }
 
     /**
@@ -735,115 +677,14 @@
      * when a thumbnail is specified with the pending animation override.
      */
     Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
-            HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode,
-            int orientation) {
-        Animation a;
-        final int thumbWidthI = thumbnailHeader.getWidth();
-        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
-        final int thumbHeightI = thumbnailHeader.getHeight();
-        final int appWidth = appRect.width();
-
-        float scaleW = appWidth / thumbWidth;
-        getNextAppTransitionStartRect(container, mTmpRect);
-        final float fromX;
-        float fromY;
-        final float toX;
-        float toY;
-        final float pivotX;
-        final float pivotY;
-        if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
-            fromX = mTmpRect.left;
-            fromY = mTmpRect.top;
-
-            // For the curved translate animation to work, the pivot points needs to be at the
-            // same absolute position as the one from the real surface.
-            toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
-            toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
-            pivotX = mTmpRect.width() / 2;
-            pivotY = appRect.height() / 2 / scaleW;
-            if (mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header is displayed above the thumbnail instead of
-                // overlapping it.
-                fromY -= thumbHeightI;
-                toY -= thumbHeightI * scaleW;
-            }
-        } else {
-            pivotX = 0;
-            pivotY = 0;
-            fromX = mTmpRect.left;
-            fromY = mTmpRect.top;
-            toX = appRect.left;
-            toY = appRect.top;
-        }
-        final long duration = getAspectScaleDuration();
-        final Interpolator interpolator = getAspectScaleInterpolator();
-        if (mNextAppTransitionScaleUp) {
-            // Animation up from the thumbnail to the full screen
-            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
-            scale.setInterpolator(interpolator);
-            scale.setDuration(duration);
-            Animation alpha = new AlphaAnimation(1f, 0f);
-            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
-            alpha.setDuration(duration);
-            Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
-            translate.setInterpolator(interpolator);
-            translate.setDuration(duration);
-
-            mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
-            mTmpToClipRect.set(appRect);
-
-            // Containing frame is in screen space, but we need the clip rect in the
-            // app space.
-            mTmpToClipRect.offsetTo(0, 0);
-            mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
-            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
-
-            if (contentInsets != null) {
-                mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
-                        (int) (-contentInsets.top * scaleW),
-                        (int) (-contentInsets.right * scaleW),
-                        (int) (-contentInsets.bottom * scaleW));
-            }
-
-            Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
-            clipAnim.setInterpolator(interpolator);
-            clipAnim.setDuration(duration);
-
-            // This AnimationSet uses the Interpolators assigned above.
-            AnimationSet set = new AnimationSet(false);
-            set.addAnimation(scale);
-            if (!mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header should be shown for the whole animation.
-                set.addAnimation(alpha);
-            }
-            set.addAnimation(translate);
-            set.addAnimation(clipAnim);
-            a = set;
-        } else {
-            // Animation down from the full screen to the thumbnail
-            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
-            scale.setInterpolator(interpolator);
-            scale.setDuration(duration);
-            Animation alpha = new AlphaAnimation(0f, 1f);
-            alpha.setInterpolator(mThumbnailFadeInInterpolator);
-            alpha.setDuration(duration);
-            Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
-            translate.setInterpolator(interpolator);
-            translate.setDuration(duration);
-
-            // This AnimationSet uses the Interpolators assigned above.
-            AnimationSet set = new AnimationSet(false);
-            set.addAnimation(scale);
-            if (!mGridLayoutRecentsEnabled) {
-                // In the grid layout, the header should be shown for the whole animation.
-                set.addAnimation(alpha);
-            }
-            set.addAnimation(translate);
-            a = set;
-
-        }
-        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
-                null);
+            HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) {
+        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+                container.hashCode());
+        return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect,
+                contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null,
+                mDefaultNextAppTransitionAnimationSpec != null
+                        ? mDefaultNextAppTransitionAnimationSpec.rect : null,
+                mNextAppTransitionScaleUp);
     }
 
     private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
@@ -1043,7 +884,7 @@
                             + "transit=%s Callers=%s",
                     a, appTransitionOldToString(transit), Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
-            a = mTransitionAnimation.createClipRevealAnimationLocked(
+            a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
                     transit, enter, frame, displayFrame,
                     mDefaultNextAppTransitionAnimationSpec != null
                             ? mDefaultNextAppTransitionAnimationSpec.rect : null);
@@ -1052,7 +893,7 @@
                             + "transit=%s Callers=%s",
                     a, appTransitionOldToString(transit), Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
-            a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
+            a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
                     mDefaultNextAppTransitionAnimationSpec != null
                             ? mDefaultNextAppTransitionAnimationSpec.rect : null);
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1064,8 +905,8 @@
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
             final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
-            a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
-                    getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
+            a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
+                    mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
                     mDefaultNextAppTransitionAnimationSpec != null
                             ? mDefaultNextAppTransitionAnimationSpec.rect : null);
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1080,9 +921,9 @@
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
             AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
                     container.hashCode());
-            a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
-                    getThumbnailTransitionState(enter), orientation, transit, frame,
-                    insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
+            a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
+                    mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
+                    stableInsets, freeform, spec != null ? spec.rect : null,
                     mDefaultNextAppTransitionAnimationSpec != null
                             ? mDefaultNextAppTransitionAnimationSpec.rect : null);
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1314,13 +1155,19 @@
     }
 
     void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+        overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */);
+    }
+
+    void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+            boolean sync) {
         ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
                         isTransitionSet(), remoteAnimationAdapter);
-        if (isTransitionSet()) {
+        if (isTransitionSet() && !mNextAppTransitionIsSync) {
             clear();
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
             mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
                     remoteAnimationAdapter, mHandler);
+            mNextAppTransitionIsSync = sync;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index faeb4ba..4355b38 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -166,6 +166,10 @@
         setReady(id, true);
     }
 
+    boolean isReady(int id) {
+        return mActiveSyncs.get(id).mReady;
+    }
+
     /**
      * Aborts the sync (ie. it doesn't wait for ready or anything to finish)
      */
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index d52e9b6..6fafc02 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -34,6 +34,7 @@
 import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
@@ -111,6 +112,7 @@
      * This method should be used for getting settings applied in each particular level of the
      * hierarchy.
      */
+    @NonNull
     public Configuration getConfiguration() {
         return mFullConfiguration;
     }
@@ -170,11 +172,13 @@
     }
 
     /** Returns requested override configuration applied to this configuration container. */
+    @NonNull
     public Configuration getRequestedOverrideConfiguration() {
         return mRequestedOverrideConfiguration;
     }
 
     /** Returns the resolved override configuration. */
+    @NonNull
     Configuration getResolvedOverrideConfiguration() {
         return mResolvedOverrideConfiguration;
     }
@@ -203,6 +207,7 @@
      * Get merged override configuration from the top of the hierarchy down to this particular
      * instance. This should be reported to client as override config.
      */
+    @NonNull
     public Configuration getMergedOverrideConfiguration() {
         return mMergedOverrideConfiguration;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index baa27e3..99f6fd4 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,8 +495,10 @@
 
 
     DisplayAreaInfo getDisplayAreaInfo() {
-        DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
+        final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
                 getDisplayContent().getDisplayId(), mFeatureId);
+        final RootDisplayArea root = getRootDisplayArea();
+        info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId;
         info.configuration.setTo(getConfiguration());
         return info;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 47d7c9d..3d7ac6c 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -25,6 +25,7 @@
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
 import android.annotation.Nullable;
 import android.os.Bundle;
@@ -135,12 +136,6 @@
  */
 class DisplayAreaPolicyBuilder {
 
-    /**
-     * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the
-     * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)}
-     */
-    static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
-
     @Nullable private HierarchyBuilder mRootHierarchyBuilder;
     private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
             new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3144c87..fef82ac 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -61,7 +61,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -94,6 +93,7 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
@@ -116,7 +116,6 @@
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 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.Task.ActivityState.RESUMED;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -166,13 +165,11 @@
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManagerInternal;
 import android.metrics.LogMaker;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
-import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -197,7 +194,6 @@
 import android.view.IWindow;
 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;
@@ -361,6 +357,13 @@
     boolean mIsSizeForced = false;
 
     /**
+     * Overridden display size and metrics to activity window bounds. Set via
+     * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging.
+     * @see WindowManagerService#setSandboxDisplayApis(int, boolean)
+     */
+    private boolean mSandboxDisplayApis = true;
+
+    /**
      * Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
      * but can be set from Settings or via shell command "adb shell wm density".
      * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
@@ -634,8 +637,6 @@
     private WindowState mParentWindow;
 
     private Point mLocationInParentWindow = new Point();
-    private SurfaceControl mParentSurfaceControl;
-    private InputWindowHandle mPortalWindowHandle;
 
     /** Corner radius that windows should have in order to match the display. */
     private final float mWindowCornerRadius;
@@ -689,6 +690,8 @@
     // well and thus won't change the top resumed / focused record
     boolean mDontMoveToTop;
 
+    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -763,6 +766,12 @@
                 mTmpWindow = null;
                 return true;
             }
+
+            if (focusedApp.getTask() == activity.getTask()
+                    && focusedApp.getTaskFragment() != activity.getTaskFragment()) {
+                // Do not use the activity window of another TaskFragment in the same leaf Task
+                return false;
+            }
         }
 
         ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
@@ -1012,6 +1021,8 @@
 
         mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
         mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
+        mAtmService.getTransitionController().registerLegacyListener(
+                mWmService.mActivityManagerAppTransitionNotifier);
         mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
         mAppTransitionController = new AppTransitionController(mWmService, this);
         mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
@@ -1909,10 +1920,6 @@
                 }
             }
         }
-
-        if (mWmService.mAccessibilityController != null) {
-            mWmService.mAccessibilityController.onRotationChanged(this);
-        }
     }
 
     void configureDisplayPolicy() {
@@ -2480,7 +2487,7 @@
 
     @Override
     boolean isVisibleRequested() {
-        return isVisible();
+        return isVisible() && !mRemoved && !mRemoving;
     }
 
     @Override
@@ -2974,7 +2981,10 @@
 
     @Override
     void removeIfPossible() {
-        if (isAnimating(TRANSITION | PARENTS)) {
+        if (isAnimating(TRANSITION | PARENTS)
+                // isAnimating is a legacy transition query and will be removed, so also add a
+                // check for whether this is in a shell-transition when not using legacy.
+                || mAtmService.getTransitionController().inTransition()) {
             mDeferredRemoval = true;
             return;
         }
@@ -3095,7 +3105,11 @@
             screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
         }
         mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
-        mAppTransition.dumpDebug(proto, APP_TRANSITION);
+        if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+            mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
+        } else {
+            mAppTransition.dumpDebug(proto, APP_TRANSITION);
+        }
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
         }
@@ -4352,14 +4366,6 @@
     private void updateBounds() {
         calculateBounds(mDisplayInfo, mTmpBounds);
         setBounds(mTmpBounds);
-        if (mPortalWindowHandle != null && mParentSurfaceControl != null) {
-            mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
-            if (!mTmpBounds.equals(mTmpRect)) {
-                mPortalWindowHandle.touchableRegion.set(mTmpBounds);
-                getPendingTransaction().setInputWindowInfo(
-                        mParentSurfaceControl, mPortalWindowHandle);
-            }
-        }
     }
 
     // Determines the current display bounds based on the current state
@@ -4499,6 +4505,8 @@
             }
         }
 
+        // clear first just in case.
+        mTmpActivityList.clear();
         // Time to remove any exiting applications?
         forAllRootTasks(task -> {
             final ArrayList<ActivityRecord> activities = task.mExitingActivities;
@@ -4506,16 +4514,24 @@
                 final ActivityRecord activity = activities.get(j);
                 if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
                         && (!activity.mIsExiting || activity.isEmpty())) {
-                    // Make sure there is no animation running on this activity, so any windows
-                    // associated with it will be removed as soon as their animations are
-                    // complete.
-                    cancelAnimation();
-                    ProtoLog.v(WM_DEBUG_ADD_REMOVE,
-                            "performLayout: Activity exiting now removed %s", activity);
-                    activity.removeIfPossible();
+                    mTmpActivityList.add(activity);
                 }
             }
         });
+        if (!mTmpActivityList.isEmpty()) {
+            // Make sure there is no animation running on this activity, so any windows
+            // associated with it will be removed as soon as their animations are
+            // complete.
+            cancelAnimation();
+        }
+        for (int i = 0; i < mTmpActivityList.size(); ++i) {
+            final ActivityRecord activity = mTmpActivityList.get(i);
+            ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+                    "performLayout: Activity exiting now removed %s", activity);
+            activity.removeIfPossible();
+        }
+        // Clear afterwards so we don't hold references.
+        mTmpActivityList.clear();
     }
 
     @Override
@@ -4962,7 +4978,7 @@
             @WindowManager.TransitionFlags int flags) {
         prepareAppTransition(transit, flags);
         mAtmService.getTransitionController().requestTransitionIfNeeded(transit, flags,
-                null /* trigger */);
+                null /* trigger */, this);
     }
 
     /** @see #requestTransitionAndLegacyPrepare(int, int) */
@@ -4970,11 +4986,11 @@
             @Nullable WindowContainer trigger) {
         prepareAppTransition(transit);
         mAtmService.getTransitionController().requestTransitionIfNeeded(transit, 0 /* flags */,
-                trigger);
+                trigger, this);
     }
 
     void executeAppTransition() {
-        mAtmService.getTransitionController().setReady();
+        mAtmService.getTransitionController().setReady(this);
         if (mAppTransition.isTransitionSet()) {
             ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
                     "Execute app transition: %s, displayId: %d Callers=%s",
@@ -4984,6 +5000,11 @@
         }
     }
 
+    void cancelAppTransition() {
+        if (!mAppTransition.isTransitionSet() || mAppTransition.isRunning()) return;
+        mAppTransition.abort();
+    }
+
     /**
      * Update pendingLayoutChanges after app transition has finished.
      */
@@ -5017,6 +5038,12 @@
 
     /** Check if pending app transition is for activity / task launch. */
     boolean isNextTransitionForward() {
+        // TODO(b/191375840): decouple "forwardness" from transition system.
+        if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+            @WindowManager.TransitionType int type =
+                    mAtmService.getTransitionController().getCollectingTransitionType();
+            return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
+        }
         return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
                 || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
     }
@@ -5340,31 +5367,6 @@
     }
 
     /**
-     * Create a portal window handle for input. This window transports any touch to the display
-     * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
-     *
-     * @param name The name of the portal window handle.
-     * @return the new portal window handle.
-     */
-    private InputWindowHandle createPortalWindowHandle(String name) {
-        // Let surface flinger to set the display ID of this input window handle because we don't
-        // know which display the parent surface control is on.
-        final InputWindowHandle portalWindowHandle = new InputWindowHandle(
-                null /* inputApplicationHandle */, INVALID_DISPLAY);
-        portalWindowHandle.name = name;
-        portalWindowHandle.token = new Binder();
-        portalWindowHandle.layoutParamsFlags =
-                FLAG_SPLIT_TOUCH | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL;
-        getBounds(mTmpBounds);
-        portalWindowHandle.touchableRegion.set(mTmpBounds);
-        portalWindowHandle.scaleFactor = 1f;
-        portalWindowHandle.ownerPid = Process.myPid();
-        portalWindowHandle.ownerUid = Process.myUid();
-        portalWindowHandle.portalToDisplayId = mDisplayId;
-        return portalWindowHandle;
-    }
-
-    /**
      * @see IWindowManager#setForwardedInsets
      */
     public void setForwardedInsets(Insets insets) {
@@ -5600,6 +5602,14 @@
                 ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
     }
 
+    @Override
+    void onResize() {
+        super.onResize();
+        if (mWmService.mAccessibilityController != null) {
+            mWmService.mAccessibilityController.onDisplaySizeChanged(this);
+        }
+    }
+
     /**
      * If the launching rotated activity ({@link #mFixedRotationLaunchingApp}) is null, it simply
      * applies the rotation to display. Otherwise because the activity has shown as rotated, the
@@ -5840,6 +5850,21 @@
         return true;
     }
 
+    /**
+     * Sets if Display APIs should be sandboxed to the activity window bounds.
+     */
+    void setSandboxDisplayApis(boolean sandboxDisplayApis) {
+        mSandboxDisplayApis = sandboxDisplayApis;
+    }
+
+    /**
+     * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds,
+     * {@code false} otherwise. Default to true, unless set for debugging purposes.
+     */
+    boolean sandboxDisplayApis() {
+        return mSandboxDisplayApis;
+    }
+
     /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
     class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 73d31bf..517d946 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,12 +16,9 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 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_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.util.RotationUtils.deltaRotation;
@@ -39,6 +36,7 @@
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.ViewRootImpl.computeWindowBounds;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -173,6 +171,9 @@
 import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -313,17 +314,41 @@
 
     private WindowState mSystemUiControllingWindow;
 
+    // Candidate window to determine the color of navigation bar. The window needs to be top
+    // fullscreen-app windows or dim layers that are intersecting with the window frame of status
+    // bar.
+    private WindowState mNavBarColorWindowCandidate;
+
+    // The window to determine opacity and background of translucent navigation bar. The window
+    // needs to be opaque.
+    private WindowState mNavBarBackgroundWindow;
+
+    /**
+     * Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
+     * the conditions of being candidate window.
+     */
+    private final ArrayList<WindowState> mStatusBarColorWindows = new ArrayList<>();
+
+    /**
+     * Windows to determine opacity and background of translucent status bar. The window needs to be
+     * opaque
+     */
+    private final ArrayList<WindowState> mStatusBarBackgroundWindows = new ArrayList<>();
+
+    private String mFocusedApp;
     private int mLastDisableFlags;
     private int mLastAppearance;
-    private int mLastFullscreenAppearance;
-    private int mLastDockedAppearance;
     private int mLastBehavior;
-    private final Rect mNonDockedRootTaskBounds = new Rect();
-    private final Rect mDockedRootTaskBounds = new Rect();
-    private final Rect mLastNonDockedRootTaskBounds = new Rect();
-    private final Rect mLastDockedRootTaskBounds = new Rect();
+    private final InsetsState mRequestedState = new InsetsState();
+    private AppearanceRegion[] mLastStatusBarAppearanceRegions;
 
-    // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+    /** The union of checked bounds while fetching {@link #mStatusBarColorWindows}. */
+    private final Rect mStatusBarColorCheckedBounds = new Rect();
+
+    /** The union of checked bounds while fetching {@link #mStatusBarBackgroundWindows}. */
+    private final Rect mStatusBarBackgroundCheckedBounds = new Rect();
+
+    // What we last reported to input dispatcher about whether the focused window is fullscreen.
     private boolean mLastFocusIsFullscreen = false;
 
     // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
@@ -333,19 +358,15 @@
     private static final Rect sTmpRect = new Rect();
     private static final Rect sTmpNavFrame = new Rect();
     private static final Rect sTmpStatusFrame = new Rect();
+    private static final Rect sTmpDecorFrame = new Rect();
     private static final Rect sTmpScreenDecorFrame = new Rect();
     private static final Rect sTmpLastParentFrame = new Rect();
     private static final Rect sTmpDisplayFrameBounds = new Rect();
 
     private WindowState mTopFullscreenOpaqueWindowState;
-    private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
-    private WindowState mTopDockedOpaqueWindowState;
-    private WindowState mTopDockedOpaqueOrDimmingWindowState;
     private boolean mTopIsFullscreen;
     private boolean mForceStatusBar;
     private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
-    private boolean mForcingShowNavBar;
-    private int mForcingShowNavBarLayer;
     private boolean mForceShowSystemBars;
 
     private boolean mShowingDream;
@@ -430,8 +451,10 @@
 
         final int displayId = displayContent.getDisplayId();
 
-        mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
-        mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+        if (!INSETS_LAYOUT_GENERALIZATION) {
+            mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
+            mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+        }
 
         final Resources r = mContext.getResources();
         mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -613,6 +636,8 @@
             }
         };
         displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
+        mService.mAtmService.getTransitionController().registerLegacyListener(
+                mAppTransitionListener);
         mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
                 mService.mVrModeEnabled);
 
@@ -1079,7 +1104,9 @@
                 mStatusBar = win;
                 final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
                         (displayFrames, windowState, rect) -> {
-                            rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+                            if (!INSETS_LAYOUT_GENERALIZATION) {
+                                rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+                            }
                         };
                 mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
                 mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
@@ -1089,18 +1116,22 @@
                 mNavigationBar = win;
                 mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
                         (displayFrames, windowState, inOutFrame) -> {
-
-                            // In Gesture Nav, navigation bar frame is larger than frame to
-                            // calculate inset.
-                            if (navigationBarPosition(displayFrames.mDisplayWidth,
-                                    displayFrames.mDisplayHeight,
-                                    displayFrames.mRotation) == NAV_BAR_BOTTOM
-                                    && !mNavButtonForcedVisible) {
-                                sTmpRect.set(inOutFrame);
-                                sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-                                inOutFrame.top = sTmpRect.bottom
-                                        - getNavigationBarHeight(displayFrames.mRotation,
-                                        mDisplayContent.getConfiguration().uiMode);
+                            if (INSETS_LAYOUT_GENERALIZATION) {
+                                inOutFrame.inset(windowState.getLayoutingAttrs(
+                                        displayFrames.mRotation).providedInternalInsets);
+                            } else {
+                                // In Gesture Nav, navigation bar frame is larger than frame to
+                                // calculate inset.
+                                if (navigationBarPosition(displayFrames.mDisplayWidth,
+                                        displayFrames.mDisplayHeight,
+                                        displayFrames.mRotation) == NAV_BAR_BOTTOM
+                                        && !mNavButtonForcedVisible) {
+                                    sTmpRect.set(inOutFrame);
+                                    sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+                                    inOutFrame.top = sTmpRect.bottom
+                                            - getNavigationBarHeight(displayFrames.mRotation,
+                                            mDisplayContent.getConfiguration().uiMode);
+                                }
                             }
                         },
 
@@ -1161,7 +1192,14 @@
                                 mExtraNavBarAltPosition = getAltBarPosition(attrs);
                                 break;
                         }
-                        mDisplayContent.setInsetProvider(insetsType, win, null);
+                        if (!INSETS_LAYOUT_GENERALIZATION) {
+                            mDisplayContent.setInsetProvider(insetsType, win, null);
+                        } else {
+                            mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
+                                    windowState, inOutFrame) -> inOutFrame.inset(
+                                            windowState.getLayoutingAttrs(displayFrames.mRotation)
+                                                    .providedInternalInsets));
+                        }
                     }
                 }
                 break;
@@ -1252,8 +1290,17 @@
     }
 
     private int getStatusBarHeight(DisplayFrames displayFrames) {
-        return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
-                displayFrames.mDisplayCutoutSafe.top);
+        int statusBarHeight;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            if (mStatusBar != null) {
+                statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
+            } else {
+                statusBarHeight = 0;
+            }
+        } else {
+            statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation];
+        }
+        return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
     }
 
     WindowState getStatusBar() {
@@ -1388,7 +1435,7 @@
     /**
      * @return true if the system bars are forced to stay visible
      */
-    public boolean areSystemBarsForcedShownLw(WindowState windowState) {
+    public boolean areSystemBarsForcedShownLw() {
         return mForceShowSystemBars;
     }
 
@@ -1423,13 +1470,30 @@
             WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames,
             Consumer<Rect> layout) {
         win.setSimulatedWindowFrames(simulatedWindowFrames);
+        final int requestedHeight = win.mRequestedHeight;
+        final int requestedWidth = win.mRequestedWidth;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            // Without a full layout process, in order to layout the system bars correctly, we need
+            // to set the requested size and the initial display frames to the window.
+            WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation);
+            win.setRequestedSize(params.width, params.height);
+            sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+            simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */,
+                    sTmpDecorFrame /* displayFrame */);
+            simulatedWindowFrames.mIsSimulatingDecorWindow = true;
+        }
         final Rect contentFrame = new Rect();
         try {
             layout.accept(contentFrame);
         } finally {
             win.setSimulatedWindowFrames(null);
+            if (INSETS_LAYOUT_GENERALIZATION) {
+                win.setRequestedSize(requestedWidth, requestedHeight);
+            }
         }
-        contentFrames.put(win.mAttrs.type, contentFrame);
+        if (!INSETS_LAYOUT_GENERALIZATION) {
+            contentFrames.put(win.mAttrs.type, contentFrame);
+        }
         mDisplayContent.getInsetsStateController().computeSimulatedState(
                 win, displayFrames, simulatedWindowFrames);
     }
@@ -1440,15 +1504,31 @@
      * some temporal states, but doesn't change the window frames used to show on screen.
      */
     void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray<Rect> barContentFrames) {
-        final WindowFrames simulatedWindowFrames = new WindowFrames();
         if (mNavigationBar != null) {
-            simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
-                    barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
-                            contentFrame));
+            final WindowFrames simulatedWindowFrames = new WindowFrames();
+            if (INSETS_LAYOUT_GENERALIZATION) {
+                simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+                        barContentFrames,
+                        contentFrame -> simulateLayoutForContentFrame(displayFrames,
+                                mNavigationBar, contentFrame));
+            } else {
+                simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+                        barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
+                                contentFrame));
+            }
         }
         if (mStatusBar != null) {
-            simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
-                    barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+            final WindowFrames simulatedWindowFrames = new WindowFrames();
+            if (INSETS_LAYOUT_GENERALIZATION) {
+                simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+                        barContentFrames,
+                        contentFrame -> simulateLayoutForContentFrame(displayFrames,
+                                mStatusBar, contentFrame));
+            } else {
+                simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+                        barContentFrames,
+                        contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+            }
         }
     }
 
@@ -1467,7 +1547,7 @@
         windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
                 sTmpStatusFrame /* displayFrame */);
         // Let the status bar determine its size.
-        mStatusBar.computeFrameAndUpdateSourceFrame();
+        mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames);
 
         // For layout, the status bar is always at the top with our fixed height.
         int statusBarBottom = displayFrames.mUnrestricted.top
@@ -1518,18 +1598,18 @@
         } else if (navBarPosition == NAV_BAR_RIGHT) {
             // Landscape screen; nav bar goes to the right.
             navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
-                    - getNavigationBarWidth(rotation, uiMode);
+                    - getNavigationBarWidth(rotation, uiMode, navBarPosition);
         } else if (navBarPosition == NAV_BAR_LEFT) {
             // Seascape screen; nav bar goes to the left.
             navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
-                    + getNavigationBarWidth(rotation, uiMode);
+                    + getNavigationBarWidth(rotation, uiMode, navBarPosition);
         }
 
         // Compute the final frame.
         final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
         windowFrames.setFrames(navigationFrame /* parentFrame */,
                 navigationFrame /* displayFrame */);
-        mNavigationBar.computeFrameAndUpdateSourceFrame();
+        mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames);
         sTmpRect.set(windowFrames.mFrame);
         sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
         contentFrame.set(sTmpRect);
@@ -1538,6 +1618,16 @@
         return navBarPosition;
     }
 
+    private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win,
+            Rect simulatedContentFrame) {
+        layoutWindowLw(win, null /* attached */, displayFrames);
+        final Rect contentFrame = sTmpRect;
+        contentFrame.set(win.getLayoutingWindowFrames().mFrame);
+        // Excluding the display cutout before set to the simulated content frame.
+        contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
+        simulatedContentFrame.set(contentFrame);
+    }
+
     private boolean canReceiveInput(WindowState win) {
         boolean notFocusable =
                 (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
@@ -1559,12 +1649,12 @@
      * @param displayFrames The display frames.
      */
     public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
-        if (win == mNavigationBar) {
+        if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
             mNavigationBarPosition = layoutNavigationBar(displayFrames,
                     mBarContentFrames.get(TYPE_NAVIGATION_BAR));
             return;
         }
-        if ((win == mStatusBar && !canReceiveInput(win))) {
+        if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
             layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
             return;
         }
@@ -1572,7 +1662,7 @@
             // Skip layout of the window when in transition to pip mode.
             return;
         }
-        final WindowManager.LayoutParams attrs = win.getAttrs();
+        final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
 
         final int type = attrs.type;
         final int fl = attrs.flags;
@@ -1580,7 +1670,7 @@
         final int sim = attrs.softInputMode;
 
         displayFrames = win.getDisplayFrames(displayFrames);
-        final WindowFrames windowFrames = win.getWindowFrames();
+        final WindowFrames windowFrames = win.getLayoutingWindowFrames();
 
         sTmpLastParentFrame.set(windowFrames.mParentFrame);
         final Rect pf = windowFrames.mParentFrame;
@@ -1591,7 +1681,13 @@
         final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
 
         final InsetsState state = win.getInsetsState();
-        computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
+        if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) {
+            // Override the bounds in window token has many side effects. Directly use the display
+            // frame set for the simulated layout for this case.
+            computeWindowBounds(attrs, state, df, df);
+        } else {
+            computeWindowBounds(attrs, state, win.getBounds(), df);
+        }
         if (attached == null) {
             pf.set(df);
             if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
@@ -1691,7 +1787,17 @@
             windowFrames.setContentChanged(true);
         }
 
-        win.computeFrameAndUpdateSourceFrame();
+        win.computeFrameAndUpdateSourceFrame(displayFrames);
+        if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) {
+            if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
+                // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+                // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+                // bar.
+                displayFrames.mDisplayCutoutSafe.top = Math.max(
+                        displayFrames.mDisplayCutoutSafe.top,
+                        windowFrames.mFrame.bottom);
+            }
+        }
     }
 
     WindowState getTopFullscreenOpaqueWindow() {
@@ -1707,12 +1813,13 @@
      */
     public void beginPostLayoutPolicyLw() {
         mTopFullscreenOpaqueWindowState = null;
-        mTopFullscreenOpaqueOrDimmingWindowState = null;
-        mTopDockedOpaqueWindowState = null;
-        mTopDockedOpaqueOrDimmingWindowState = null;
+        mNavBarColorWindowCandidate = null;
+        mNavBarBackgroundWindow = null;
+        mStatusBarColorWindows.clear();
+        mStatusBarBackgroundWindows.clear();
+        mStatusBarColorCheckedBounds.setEmpty();
+        mStatusBarBackgroundCheckedBounds.setEmpty();
         mForceStatusBar = false;
-        mForcingShowNavBar = false;
-        mForcingShowNavBarLayer = -1;
 
         mAllowLockscreenWhenOn = false;
         mShowingDream = false;
@@ -1731,20 +1838,22 @@
         final boolean affectsSystemUi = win.canAffectSystemUiFlags();
         if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
         applyKeyguardPolicy(win, imeTarget);
-        final int fl = attrs.flags;
-        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
-                && attrs.type == TYPE_INPUT_METHOD) {
-            mForcingShowNavBar = true;
-            mForcingShowNavBarLayer = win.getSurfaceLayer();
+
+        // Check if the freeform window overlaps with the navigation bar area.
+        final boolean isOverlappingWithNavBar = isOverlappingWithNavBar(win, mNavigationBar);
+        if (isOverlappingWithNavBar && !mIsFreeformWindowOverlappingWithNavBar
+                && win.inFreeformWindowingMode()) {
+            mIsFreeformWindowOverlappingWithNavBar = true;
+        }
+
+        if (!affectsSystemUi) {
+            return;
         }
 
         boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
                 && attrs.type < FIRST_SYSTEM_WINDOW;
-        final int windowingMode = win.getWindowingMode();
-        final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
-                windowingMode == WINDOWING_MODE_FULLSCREEN
-                        || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-        if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
+        if (mTopFullscreenOpaqueWindowState == null) {
+            final int fl = attrs.flags;
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
                 mForceStatusBar = true;
             }
@@ -1757,68 +1866,60 @@
                 }
             }
 
-            // For app windows that are not attached, we decide if all windows in the app they
-            // represent should be hidden or if we should hide the lockscreen. For attached app
-            // windows we defer the decision to the window it is attached to.
-            if (appWindow && attached == null) {
-                if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
-                    mTopFullscreenOpaqueWindowState = win;
-                    if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
-                        mTopFullscreenOpaqueOrDimmingWindowState = win;
-                    }
-                    if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
-                        mAllowLockscreenWhenOn = true;
-                    }
-                }
+            if (appWindow && attached == null && attrs.isFullscreen()
+                    && (fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+                mAllowLockscreenWhenOn = true;
             }
         }
 
-        // Voice interaction overrides both top fullscreen and top docked.
-        if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION && attrs.isFullscreen()) {
+        // Check the windows that overlap with system bars to determine system bars' appearance.
+        if ((appWindow && attached == null && attrs.isFullscreen())
+                || attrs.type == TYPE_VOICE_INTERACTION) {
+            // Record the top-fullscreen-app-window which will be used to determine system UI
+            // controlling window.
             if (mTopFullscreenOpaqueWindowState == null) {
                 mTopFullscreenOpaqueWindowState = win;
-                if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
-                    mTopFullscreenOpaqueOrDimmingWindowState = win;
+            }
+
+            // Cache app windows that is overlapping with the status bar to determine appearance
+            // of status bar.
+            if (mStatusBar != null
+                    && sTmpRect.setIntersect(win.getFrame(), mStatusBar.getFrame())
+                    && !mStatusBarBackgroundCheckedBounds.contains(sTmpRect)) {
+                mStatusBarBackgroundWindows.add(win);
+                mStatusBarBackgroundCheckedBounds.union(sTmpRect);
+                if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+                    mStatusBarColorWindows.add(win);
+                    mStatusBarColorCheckedBounds.union(sTmpRect);
                 }
             }
-            if (mTopDockedOpaqueWindowState == null) {
-                mTopDockedOpaqueWindowState = win;
-                if (mTopDockedOpaqueOrDimmingWindowState == null) {
-                    mTopDockedOpaqueOrDimmingWindowState = win;
+
+            // Cache app window that overlaps with the navigation bar area to determine opacity
+            // and appearance of the navigation bar. We only need to cache one window because
+            // there should be only one overlapping window if it's not in gesture navigation
+            // mode; if it's in gesture navigation mode, the navigation bar will be
+            // NAV_BAR_FORCE_TRANSPARENT and its appearance won't be decided by overlapping
+            // windows.
+            if (isOverlappingWithNavBar) {
+                if (mNavBarColorWindowCandidate == null) {
+                    mNavBarColorWindowCandidate = win;
+                }
+                if (mNavBarBackgroundWindow == null) {
+                    mNavBarBackgroundWindow = win;
                 }
             }
-        }
-
-        // Keep track of the window if it's dimming but not necessarily fullscreen.
-        if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
-                && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
-            mTopFullscreenOpaqueOrDimmingWindowState = win;
-        }
-
-        // We need to keep track of the top "fullscreen" opaque window for the docked root task
-        // separately, because both the "real fullscreen" opaque window and the one for the docked
-        // root task can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
-        if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
-                && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mTopDockedOpaqueWindowState = win;
-            if (mTopDockedOpaqueOrDimmingWindowState == null) {
-                mTopDockedOpaqueOrDimmingWindowState = win;
+        } else if (win.isDimming()) {
+            // For dimming window whose host bounds is overlapping with system bars, it can be
+            // used to determine colors but not opacity of system bars.
+            if (mStatusBar != null
+                    && sTmpRect.setIntersect(win.getBounds(), mStatusBar.getFrame())
+                    && !mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+                mStatusBarColorWindows.add(win);
+                mStatusBarColorCheckedBounds.union(sTmpRect);
             }
-        }
-
-        // Check if the freeform window overlaps with the navigation bar area.
-        final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null;
-        if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()
-                && isOverlappingWithNavBar(win, navBarWin)) {
-            mIsFreeformWindowOverlappingWithNavBar = true;
-        }
-
-        // Also keep track of any windows that are dimming but not necessarily fullscreen in the
-        // docked root task.
-        if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
-                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            mTopDockedOpaqueOrDimmingWindowState = win;
+            if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
+                mNavBarColorWindowCandidate = win;
+            }
         }
     }
 
@@ -2120,18 +2221,47 @@
         return mUiContext;
     }
 
-    private int getNavigationBarWidth(int rotation, int uiMode) {
-        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
-            return mNavigationBarWidthForRotationInCarMode[rotation];
+    private int getNavigationBarWidth(int rotation, int uiMode, int position) {
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            if (mNavigationBar == null) {
+                return 0;
+            }
+            LayoutParams lp = mNavigationBar.mAttrs;
+            if (lp.paramsForRotation != null
+                    && lp.paramsForRotation.length == 4
+                    && lp.paramsForRotation[rotation] != null) {
+                lp = lp.paramsForRotation[rotation];
+            }
+            if (position == NAV_BAR_LEFT) {
+                if (lp.width > lp.providedInternalInsets.right) {
+                    return lp.width - lp.providedInternalInsets.right;
+                } else {
+                    return 0;
+                }
+            } else if (position == NAV_BAR_RIGHT) {
+                if (lp.width > lp.providedInternalInsets.left) {
+                    return lp.width - lp.providedInternalInsets.left;
+                } else {
+                    return 0;
+                }
+            }
+            return lp.width;
         } else {
-            return mNavigationBarWidthForRotationDefault[rotation];
+            if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+                return mNavigationBarWidthForRotationInCarMode[rotation];
+            } else {
+                return mNavigationBarWidthForRotationDefault[rotation];
+            }
         }
     }
 
     void notifyDisplayReady() {
         mHandler.post(() -> {
             final int displayId = getDisplayId();
-            getStatusBarManagerInternal().onDisplayReady(displayId);
+            StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+            if (statusBar != null) {
+                statusBar.onDisplayReady(displayId);
+            }
             final WallpaperManagerInternal wpMgr = LocalServices
                     .getService(WallpaperManagerInternal.class);
             if (wpMgr != null) {
@@ -2151,7 +2281,7 @@
         if (hasNavigationBar()) {
             final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
             if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
-                width -= getNavigationBarWidth(rotation, uiMode);
+                width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
             }
         }
         if (displayCutout != null) {
@@ -2161,10 +2291,21 @@
     }
 
     private int getNavigationBarHeight(int rotation, int uiMode) {
-        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
-            return mNavigationBarHeightForRotationInCarMode[rotation];
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            if (mNavigationBar == null) {
+                return 0;
+            }
+            LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
+            if (lp.height < lp.providedInternalInsets.top) {
+                return 0;
+            }
+            return lp.height - lp.providedInternalInsets.top;
         } else {
-            return mNavigationBarHeightForRotationDefault[rotation];
+            if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+                return mNavigationBarHeightForRotationInCarMode[rotation];
+            } else {
+                return mNavigationBarHeightForRotationDefault[rotation];
+            }
         }
     }
 
@@ -2181,10 +2322,17 @@
      * @return navigation bar frame height
      */
     private int getNavigationBarFrameHeight(int rotation, int uiMode) {
-        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
-            return mNavigationBarHeightForRotationInCarMode[rotation];
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            if (mNavigationBar == null) {
+                return 0;
+            }
+            return mNavigationBar.mAttrs.height;
         } else {
-            return mNavigationBarFrameHeightForRotationDefault[rotation];
+            if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+                return mNavigationBarHeightForRotationInCarMode[rotation];
+            } else {
+                return mNavigationBarFrameHeightForRotationDefault[rotation];
+            }
         }
     }
 
@@ -2305,9 +2453,9 @@
             if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
             } else if (position == NAV_BAR_RIGHT) {
-                outInsets.right = getNavigationBarWidth(displayRotation, uiMode);
+                outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
             } else if (position == NAV_BAR_LEFT) {
-                outInsets.left = getNavigationBarWidth(displayRotation, uiMode);
+                outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
             }
         }
 
@@ -2333,6 +2481,17 @@
 
     @NavigationBarPosition
     int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+        if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) {
+            final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity;
+            switch (gravity) {
+                case Gravity.LEFT:
+                    return NAV_BAR_LEFT;
+                case Gravity.RIGHT:
+                    return NAV_BAR_RIGHT;
+                default:
+                    return NAV_BAR_BOTTOM;
+            }
+        }
         if (navigationBarCanMove() && displayWidth > displayHeight) {
             if (displayRotation == Surface.ROTATION_270) {
                 return NAV_BAR_LEFT;
@@ -2490,45 +2649,34 @@
         final WindowState win = winCandidate;
         mSystemUiControllingWindow = win;
 
-        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
-
-        final boolean inSplitScreen =
-                mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
-        if (inSplitScreen) {
-            mService.getRootTaskBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                    mDockedRootTaskBounds);
-        } else {
-            mDockedRootTaskBounds.setEmpty();
-        }
-        mService.getRootTaskBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        : WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_UNDEFINED, mNonDockedRootTaskBounds);
-        final int fullscreenAppearance = getStatusBarAppearance(mTopFullscreenOpaqueWindowState,
-                mTopFullscreenOpaqueOrDimmingWindowState);
-        final int dockedAppearance = getStatusBarAppearance(mTopDockedOpaqueWindowState,
-                mTopDockedOpaqueOrDimmingWindowState);
         final int disableFlags = win.getDisableFlags();
         final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
-        final WindowState navColorWin = chooseNavigationColorWindowLw(
-                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+        final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
                 mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
         final boolean isNavbarColorManagedByIme =
                 navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
-        final int appearance = updateLightNavigationBarLw(
-                win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
-                mTopFullscreenOpaqueOrDimmingWindowState,
-                mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
+        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
+                navColorWin) | opaqueAppearance;
         final int behavior = win.mAttrs.insetsFlags.behavior;
         final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                 || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
+
+        final AppearanceRegion[] appearanceRegions =
+                new AppearanceRegion[mStatusBarColorWindows.size()];
+        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+            final WindowState windowState = mStatusBarColorWindows.get(i);
+            appearanceRegions[i] = new AppearanceRegion(
+                    getStatusBarAppearance(windowState, windowState),
+                    new Rect(windowState.getFrame()));
+        }
+
         if (mLastDisableFlags == disableFlags
                 && mLastAppearance == appearance
-                && mLastFullscreenAppearance == fullscreenAppearance
-                && mLastDockedAppearance == dockedAppearance
                 && mLastBehavior == behavior
+                && mRequestedState.equals(win.getRequestedState())
+                && Objects.equals(mFocusedApp, win.mAttrs.packageName)
                 && mLastFocusIsFullscreen == isFullscreen
-                && mLastNonDockedRootTaskBounds.equals(mNonDockedRootTaskBounds)
-                && mLastDockedRootTaskBounds.equals(mDockedRootTaskBounds)) {
+                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
             return false;
         }
         if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
@@ -2538,28 +2686,19 @@
         }
         mLastDisableFlags = disableFlags;
         mLastAppearance = appearance;
-        mLastFullscreenAppearance = fullscreenAppearance;
-        mLastDockedAppearance = dockedAppearance;
         mLastBehavior = behavior;
+        mRequestedState.set(win.getRequestedState(), true /* copySources */);
+        mFocusedApp = win.mAttrs.packageName;
         mLastFocusIsFullscreen = isFullscreen;
-        mLastNonDockedRootTaskBounds.set(mNonDockedRootTaskBounds);
-        mLastDockedRootTaskBounds.set(mDockedRootTaskBounds);
-        final Rect fullscreenRootTaskBounds = new Rect(mNonDockedRootTaskBounds);
-        final Rect dockedRootTaskBounds = new Rect(mDockedRootTaskBounds);
-        final AppearanceRegion[] appearanceRegions = inSplitScreen
-                ? new AppearanceRegion[]{
-                        new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds),
-                        new AppearanceRegion(dockedAppearance, dockedRootTaskBounds)}
-                : new AppearanceRegion[]{
-                        new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds)};
-        String cause = win.toString();
+        mLastStatusBarAppearanceRegions = appearanceRegions;
+        final String cause = win.toString();
         mHandler.post(() -> {
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
             if (statusBar != null) {
                 final int displayId = getDisplayId();
                 statusBar.setDisableFlags(displayId, disableFlags, cause);
                 statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
-                        isNavbarColorManagedByIme, behavior, isFullscreen);
+                        isNavbarColorManagedByIme, behavior, mRequestedState, mFocusedApp);
 
             }
         });
@@ -2576,8 +2715,7 @@
 
     @VisibleForTesting
     @Nullable
-    static WindowState chooseNavigationColorWindowLw(WindowState opaque,
-            WindowState opaqueOrDimming, WindowState imeWindow,
+    static WindowState chooseNavigationColorWindowLw(WindowState candidate, WindowState imeWindow,
             @NavigationBarPosition int navBarPosition) {
         // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
         // window can be navigation color window.
@@ -2586,71 +2724,59 @@
                 && navBarPosition == NAV_BAR_BOTTOM
                 && (imeWindow.mAttrs.flags
                         & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-
-        if (opaque != null && opaqueOrDimming == opaque) {
-            // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
-            // unless IME window is also eligible, since currently the IME window is always show
-            // above the opaque fullscreen app window, regardless of the IME target window.
-            // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
-            return imeWindowCanNavColorWindow ? imeWindow : opaque;
-        }
-
-        if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
-            // No dimming window is involved. Determine the result only with the IME window.
-            return imeWindowCanNavColorWindow ? imeWindow : null;
-        }
-
         if (!imeWindowCanNavColorWindow) {
-            // No IME window is involved. Determine the result only with opaqueOrDimming.
-            return opaqueOrDimming;
+            // No IME window is involved. Determine the result only with candidate window.
+            return candidate;
         }
 
-        // The IME window and the dimming window are competing.  Check if the dimming window can be
-        // IME target or not.
-        if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) {
-            // The IME window is above the dimming window.
-            return imeWindow;
-        } else {
-            // The dimming window is above the IME window.
-            return opaqueOrDimming;
+        if (candidate != null && candidate.isDimming()) {
+            // The IME window and the dimming window are competing. Check if the dimming window can
+            // be IME target or not.
+            if (LayoutParams.mayUseInputMethod(candidate.mAttrs.flags)) {
+                // The IME window is above the dimming window.
+                return imeWindow;
+            } else {
+                // The dimming window is above the IME window.
+                return candidate;
+            }
         }
+
+        return imeWindow;
     }
 
     @VisibleForTesting
-    int updateLightNavigationBarLw(int appearance, WindowState opaque,
-            WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
-
-        if (navColorWin != null) {
-            if (navColorWin == imeWindow || navColorWin == opaque) {
-                // Respect the light flag.
-                appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
-                appearance |= navColorWin.mAttrs.insetsFlags.appearance
-                        & APPEARANCE_LIGHT_NAVIGATION_BARS;
-            } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
-                // Clear the light flag for dimming window.
-                appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
-            }
-        }
-        if (!isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+    int updateLightNavigationBarLw(int appearance, WindowState navColorWin) {
+        if (navColorWin == null || navColorWin.isDimming()
+                || !isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+            // Clear the light flag while not allowed.
             appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+            return appearance;
         }
+
+        // Respect the light flag of navigation color window.
+        appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+        appearance |= navColorWin.mAttrs.insetsFlags.appearance
+                & APPEARANCE_LIGHT_NAVIGATION_BARS;
         return appearance;
     }
 
     private int updateSystemBarsLw(WindowState win, int disableFlags) {
-        final boolean dockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
+        final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+        final boolean multiWindowTaskVisible =
+                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                        || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
+        final boolean freeformRootTaskVisible =
+                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
 
-        // We need to force system bars when the docked root task is visible, when the freeform
-        // root task is focused but also when we are resizing for the transitions when docked
-        // root task visibility changes.
-        mForceShowSystemBars = dockedRootTaskVisible || win.inFreeformWindowingMode() || resizing;
+        // We need to force showing system bars when the multi-window or freeform root task is
+        // visible.
+        mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
 
         int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
-
         appearance = configureStatusBarOpacity(appearance);
-        appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible, resizing);
+        appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
+                freeformRootTaskVisible);
 
         final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
         final long now = SystemClock.uptimeMillis();
@@ -2696,7 +2822,24 @@
 
     private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
         final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
-        return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType);
+        if (rotatedBarFrame != null) {
+            return rotatedBarFrame;
+        }
+        if (!INSETS_LAYOUT_GENERALIZATION) {
+            return mBarContentFrames.get(windowType);
+        }
+        // We only need a window specific information for the fixed rotation, use raw insets state
+        // for all other cases.
+        InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        final Rect tmpRect = new Rect();
+        if (windowType == TYPE_NAVIGATION_BAR) {
+            tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame());
+        }
+        if (windowType == TYPE_STATUS_BAR) {
+            tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame());
+        }
+        tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe);
+        return tmpRect;
     }
 
     /**
@@ -2731,17 +2874,19 @@
 
     /** @return the current visibility flags with the status bar opacity related flags toggled. */
     private int configureStatusBarOpacity(int appearance) {
-        final boolean fullscreenDrawsBackground =
-                drawsBarBackground(mTopFullscreenOpaqueWindowState);
-        final boolean dockedDrawsBackground =
-                drawsBarBackground(mTopDockedOpaqueWindowState);
+        boolean drawBackground = true;
+        boolean isFullyTransparentAllowed = true;
+        for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+            final WindowState window = mStatusBarBackgroundWindows.get(i);
+            drawBackground &= drawsBarBackground(window);
+            isFullyTransparentAllowed &= isFullyTransparentAllowed(window, TYPE_STATUS_BAR);
+        }
 
-        if (fullscreenDrawsBackground && dockedDrawsBackground) {
+        if (drawBackground) {
             appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
         }
 
-        if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_STATUS_BAR)
-                || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_STATUS_BAR)) {
+        if (!isFullyTransparentAllowed) {
             appearance |= APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
         }
 
@@ -2752,53 +2897,35 @@
      * @return the current visibility flags with the nav-bar opacity related flags toggled based
      *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
      */
-    private int configureNavBarOpacity(int appearance, boolean dockedRootTaskVisible,
-            boolean isDockedDividerResizing) {
-        final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
-        final boolean fullscreenDrawsBackground =
-                drawsBarBackground(mTopFullscreenOpaqueWindowState);
-        final boolean dockedDrawsBackground =
-                drawsBarBackground(mTopDockedOpaqueWindowState);
+    private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible,
+            boolean freeformRootTaskVisible) {
+        final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow);
 
         if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
-            if (fullscreenDrawsBackground && dockedDrawsBackground) {
+            if (drawBackground) {
                 appearance = clearNavBarOpaqueFlag(appearance);
-            } else if (dockedRootTaskVisible) {
-                appearance = setNavBarOpaqueFlag(appearance);
             }
         } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
-            if (dockedRootTaskVisible || freeformRootTaskVisible || isDockedDividerResizing) {
+            if (multiWindowTaskVisible || freeformRootTaskVisible) {
                 if (mIsFreeformWindowOverlappingWithNavBar) {
                     appearance = clearNavBarOpaqueFlag(appearance);
-                } else {
-                    appearance = setNavBarOpaqueFlag(appearance);
                 }
-            } else if (fullscreenDrawsBackground) {
+            } else if (drawBackground) {
                 appearance = clearNavBarOpaqueFlag(appearance);
             }
         } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
-            if (isDockedDividerResizing) {
-                appearance = setNavBarOpaqueFlag(appearance);
-            } else if (freeformRootTaskVisible) {
+            if (freeformRootTaskVisible) {
                 appearance = clearNavBarOpaqueFlag(appearance);
-            } else {
-                appearance = setNavBarOpaqueFlag(appearance);
             }
         }
 
-        if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR)
-                || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) {
+        if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, TYPE_NAVIGATION_BAR)) {
             appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
         }
 
         return appearance;
     }
 
-    private int setNavBarOpaqueFlag(int appearance) {
-        return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS;
-    }
-
     private int clearNavBarOpaqueFlag(int appearance) {
         return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS;
     }
@@ -2899,6 +3026,7 @@
     void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.println("DisplayPolicy");
         prefix += "  ";
+        final String prefixInner = prefix + "  ";
         pw.print(prefix);
         pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer);
         pw.print(" mDeskDockEnablesAccelerometer=");
@@ -2966,14 +3094,27 @@
             pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
             pw.println(mTopFullscreenOpaqueWindowState);
         }
-        if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
-            pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
-            pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
+        if (mNavBarColorWindowCandidate != null) {
+            pw.print(prefix); pw.print("mNavBarColorWindowCandidate=");
+            pw.println(mNavBarColorWindowCandidate);
         }
-        if (mForcingShowNavBar) {
-            pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar);
-            pw.print(prefix); pw.print("mForcingShowNavBarLayer=");
-            pw.println(mForcingShowNavBarLayer);
+        if (mNavBarBackgroundWindow != null) {
+            pw.print(prefix); pw.print("mNavBarBackgroundWindow=");
+            pw.println(mNavBarBackgroundWindow);
+        }
+        if (!mStatusBarColorWindows.isEmpty()) {
+            pw.print(prefix); pw.println("mStatusBarColorWindows=");
+            for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+                final WindowState win = mStatusBarColorWindows.get(i);
+                pw.print(prefixInner); pw.println(win);
+            }
+        }
+        if (!mStatusBarBackgroundWindows.isEmpty()) {
+            pw.print(prefix); pw.println("mStatusBarBackgroundWindows=");
+            for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+                final WindowState win = mStatusBarBackgroundWindows.get(i);
+                pw.print(prefixInner);  pw.println(win);
+            }
         }
         pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
         pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
@@ -3011,7 +3152,7 @@
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
         lp.setFitInsetsTypes(0);
-        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         if (ActivityManager.isHighEndGfx()) {
             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
             lp.privateFlags |=
@@ -3055,13 +3196,17 @@
     }
 
     @VisibleForTesting
-    static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
+    static boolean isOverlappingWithNavBar(@NonNull WindowState targetWindow,
+            WindowState navBarWindow) {
         if (navBarWindow == null || !navBarWindow.isVisible()
                 || targetWindow.mActivityRecord == null || !targetWindow.isVisible()) {
             return false;
         }
 
-        return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame());
+        // When the window is dimming means it's requesting dim layer to its host container, so
+        // checking whether it's overlapping with navigation bar by its container's bounds.
+        return Rect.intersects(targetWindow.isDimming()
+                ? targetWindow.getBounds() : targetWindow.getFrame(), navBarWindow.getFrame());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 316c20b..45f401b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.Task.TAG_VISIBILITY;
 
@@ -24,22 +25,22 @@
 
 /** Helper class to ensure activities are in the right visible state for a container. */
 class EnsureActivitiesVisibleHelper {
-    private final Task mTask;
+    private final TaskFragment mTaskFragment;
     private ActivityRecord mTop;
     private ActivityRecord mStarting;
     private boolean mAboveTop;
     private boolean mContainerShouldBeVisible;
-    private boolean mBehindFullscreenActivity;
+    private boolean mBehindFullyOccludedContainer;
     private int mConfigChanges;
     private boolean mPreserveWindows;
     private boolean mNotifyClients;
 
-    EnsureActivitiesVisibleHelper(Task container) {
-        mTask = container;
+    EnsureActivitiesVisibleHelper(TaskFragment container) {
+        mTaskFragment = container;
     }
 
     /**
-     * Update all attributes except {@link mTask} to use in subsequent calculations.
+     * Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
      *
      * @param starting The activity that is being started
      * @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -51,12 +52,12 @@
     void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
             boolean notifyClients) {
         mStarting = starting;
-        mTop = mTask.topRunningActivity();
+        mTop = mTaskFragment.topRunningActivity();
         // If the top activity is not fullscreen, then we need to make sure any activities under it
         // are now visible.
         mAboveTop = mTop != null;
-        mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
-        mBehindFullscreenActivity = !mContainerShouldBeVisible;
+        mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
+        mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
         mConfigChanges = configChanges;
         mPreserveWindows = preserveWindows;
         mNotifyClients = notifyClients;
@@ -85,22 +86,35 @@
             Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
                     + " configChanges=0x" + Integer.toHexString(configChanges));
         }
-        if (mTop != null) {
-            mTask.checkTranslucentActivityWaiting(mTop);
+        if (mTop != null && mTaskFragment.asTask() != null) {
+            // TODO(14709632): Check if this needed to be implemented in TaskFragment.
+            mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop);
         }
 
         // We should not resume activities that being launched behind because these
         // activities are actually behind other fullscreen activities, but still required
         // to be visible (such as performing Recents animation).
         final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
-                && mTask.isTopActivityFocusable()
-                && (starting == null || !starting.isDescendantOf(mTask));
+                && mTaskFragment.isTopActivityFocusable()
+                && (starting == null || !starting.isDescendantOf(mTaskFragment));
 
-        mTask.forAllActivities(a -> {
-            setActivityVisibilityState(a, starting, resumeTopActivity);
-        });
-        if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
-            mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+        for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mTaskFragment.mChildren.get(i);
+            if (child.asTaskFragment() != null) {
+                final TaskFragment childTaskFragment = child.asTaskFragment();
+                childTaskFragment.updateActivityVisibilities(starting, configChanges,
+                        preserveWindows, notifyClients);
+                mBehindFullyOccludedContainer = childTaskFragment.getBounds().equals(
+                        mTaskFragment.getBounds());
+                if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
+                    mAboveTop = false;
+                }
+            } else if (child.asActivityRecord() != null) {
+                setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
+            }
+        }
+        if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+            mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
         }
     }
 
@@ -112,7 +126,7 @@
         }
         mAboveTop = false;
 
-        r.updateVisibilityIgnoringKeyguard(mBehindFullscreenActivity);
+        r.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer);
         final boolean reallyVisible = r.shouldBeVisibleUnchecked();
 
         // Check whether activity should be visible without Keyguard influence
@@ -122,12 +136,14 @@
                 if (DEBUG_VISIBILITY) {
                     Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
                             + " containerVisible=" + mContainerShouldBeVisible
-                            + " behindFullscreen=" + mBehindFullscreenActivity);
+                            + " behindFullyOccluded=" + mBehindFullyOccludedContainer);
                 }
-                mBehindFullscreenActivity = true;
+                mBehindFullyOccludedContainer = true;
             } else {
-                mBehindFullscreenActivity = false;
+                mBehindFullyOccludedContainer = false;
             }
+        } else if (r.isState(INITIALIZING)) {
+            r.cancelInitializing();
         }
 
         if (reallyVisible) {
@@ -173,24 +189,25 @@
                 Slog.v(TAG_VISIBILITY, "Make invisible? " + r
                         + " finishing=" + r.finishing + " state=" + r.getState()
                         + " containerShouldBeVisible=" + mContainerShouldBeVisible
-                        + " behindFullscreenActivity=" + mBehindFullscreenActivity
+                        + " behindFullyOccludedContainer=" + mBehindFullyOccludedContainer
                         + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
             }
             r.makeInvisible();
         }
 
-        if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
+        if (!mBehindFullyOccludedContainer && mTaskFragment.isActivityTypeHome()
+                && r.isRootOfTask()) {
             if (DEBUG_VISIBILITY) {
-                Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
+                Slog.v(TAG_VISIBILITY, "Home task: at " + mTaskFragment
                         + " containerShouldBeVisible=" + mContainerShouldBeVisible
-                        + " behindFullscreenActivity=" + mBehindFullscreenActivity);
+                        + " behindOccludedParentContainer=" + mBehindFullyOccludedContainer);
             }
             // No other task in the root home task should be visible behind the home activity.
             // Home activities is usually a translucent activity with the wallpaper behind
             // them. However, when they don't have the wallpaper behind them, we want to
             // show activities in the next application root task behind them vs. another
             // task in the root home task like recents.
-            mBehindFullscreenActivity = true;
+            mBehindFullyOccludedContainer = true;
         }
     }
 
@@ -219,7 +236,8 @@
             r.setVisibility(true);
         }
         if (r != starting) {
-            mTask.mTaskSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+            mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
+                    true /* checkConfig */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index da47328..4f6a693 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -29,6 +29,7 @@
 import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSource;
@@ -90,6 +91,16 @@
         onSourceChanged();
     }
 
+    @Override
+    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+        if (target != null && target.getWindow() != null) {
+            // ime control target could be a different window.
+            // Refer WindowState#getImeControlTarget().
+            target = target.getWindow().getImeControlTarget();
+        }
+        super.updateControlForTarget(target, force);
+    }
+
     private void onSourceChanged() {
         if (mLastSource.equals(mSource)) {
             return;
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 747d365..f3b9cdf 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
@@ -420,7 +421,7 @@
         }
 
         final Bundle options = new Bundle();
-        options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+        options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
         return options;
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c781a1..eb5ab83 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
@@ -617,7 +616,6 @@
         inputWindowHandle.setScaleFactor(1f);
         inputWindowHandle.setLayoutParamsFlags(
                 FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
-        inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY);
         inputWindowHandle.clearTouchableRegion();
         inputWindowHandle.setTouchableRegionCrop(null);
     }
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 7a4d13c..b4e11ff 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -223,14 +223,6 @@
         mChanged = true;
     }
 
-    void setPortalToDisplayId(int displayId) {
-        if (mHandle.portalToDisplayId == displayId) {
-            return;
-        }
-        mHandle.portalToDisplayId = displayId;
-        mChanged = true;
-    }
-
     void setFrame(int left, int top, int right, int bottom) {
         if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right
                 && mHandle.frameBottom == bottom) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f2f1926..d9112c5 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -18,8 +18,6 @@
 
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
@@ -32,6 +30,10 @@
 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 static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.StatusBarManager;
@@ -55,6 +57,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.DisplayThread;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 /**
  * Policy that implements who gets control over the windows generating insets.
@@ -135,11 +138,8 @@
             abortTransient();
         }
         mFocusedWin = focusedWin;
-        boolean forceShowsSystemBarsForWindowingMode = forceShowsSystemBarsForWindowingMode();
-        InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin,
-                forceShowsSystemBarsForWindowingMode);
-        InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin,
-                forceShowsSystemBarsForWindowingMode);
+        InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin);
+        InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin);
         mStateController.onBarControlTargetChanged(statusControlTarget,
                 getFakeControlTarget(focusedWin, statusControlTarget),
                 navControlTarget,
@@ -167,8 +167,12 @@
             changed = true;
         }
         if (changed) {
-            mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
-                    mShowingTransientTypes.toArray());
+            StatusBarManagerInternal statusBarManagerInternal =
+                    mPolicy.getStatusBarManagerInternal();
+            if (statusBarManagerInternal != null) {
+                statusBarManagerInternal.showTransient(
+                        mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+            }
             updateBarControlTarget(mFocusedWin);
 
             // The leashes can be created while updating bar control target. The surface transaction
@@ -211,7 +215,7 @@
     InsetsState getInsetsForWindow(WindowState target) {
         final InsetsState originalState = mStateController.getInsetsForWindow(target);
         final InsetsState state = adjustVisibilityForTransientTypes(originalState);
-        return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
+        return adjustVisibilityForIme(target, state, state == originalState);
     }
 
     /**
@@ -241,16 +245,38 @@
         return state;
     }
 
-    // Navigation bar insets is always visible to IME.
-    private static InsetsState adjustVisibilityForIme(InsetsState originalState,
+    private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
             boolean copyState) {
-        final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
-        if (originalNavSource != null && !originalNavSource.isVisible()) {
-            final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
-            final InsetsSource navSource = new InsetsSource(originalNavSource);
-            navSource.setVisible(true);
-            state.addSource(navSource);
-            return state;
+        if (w.mIsImWindow) {
+            // Navigation bar insets is always visible to IME.
+            final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
+            if (originalNavSource != null && !originalNavSource.isVisible()) {
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+                final InsetsSource navSource = new InsetsSource(originalNavSource);
+                navSource.setVisible(true);
+                state.addSource(navSource);
+                return state;
+            }
+        } else if (w.mActivityRecord != null && !w.mActivityRecord.mLastImeShown) {
+            // During switching tasks with gestural navigation, if the IME is attached to
+            // one app window on that time, even the next app window is behind the IME window,
+            // conceptually the window should not receive the IME insets if the next window is
+            // not eligible IME requester and ready to show IME on top of it.
+            final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
+            final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
+
+            if (originalImeSource != null && shouldImeAttachedToApp
+                    && (w.isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS)
+                            || !w.getRequestedVisibility(ITYPE_IME))) {
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+
+                final InsetsSource imeSource = new InsetsSource(originalImeSource);
+                imeSource.setVisible(false);
+                state.addSource(imeSource);
+                return state;
+            }
         }
         return originalState;
     }
@@ -282,9 +308,11 @@
                     abortTypes.add(type);
                 }
             }
-            if (abortTypes.size() > 0) {
-                mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
-                        abortTypes.toArray());
+            StatusBarManagerInternal statusBarManagerInternal =
+                    mPolicy.getStatusBarManagerInternal();
+            if (abortTypes.size() > 0 && statusBarManagerInternal != null) {
+                statusBarManagerInternal.abortTransient(
+                        mDisplayContent.getDisplayId(), abortTypes.toArray());
             }
         }
     }
@@ -294,8 +322,11 @@
      * updateBarControlTarget(mFocusedWin) after this invocation.
      */
     private void abortTransient() {
-        mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
-                mShowingTransientTypes.toArray());
+        StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal();
+        if (statusBarManagerInternal != null) {
+            statusBarManagerInternal.abortTransient(
+                    mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+        }
         mShowingTransientTypes.clear();
     }
 
@@ -304,8 +335,7 @@
         return realControlTarget == mDummyControlTarget ? focused : null;
     }
 
-    private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
-            boolean forceShowsSystemBarsForWindowingMode) {
+    private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin) {
         if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) {
             return mDummyControlTarget;
         }
@@ -319,10 +349,9 @@
                     focusedWin.mAttrs.packageName);
             return mDisplayContent.mRemoteInsetsControlTarget;
         }
-        if (forceShowsSystemBarsForWindowingMode) {
-            // Status bar is forcibly shown for the windowing mode which is a steady state.
-            // We don't want the client to control the status bar, and we will dispatch the real
-            // visibility of status bar to the client.
+        if (mPolicy.areSystemBarsForcedShownLw()) {
+            // Status bar is forcibly shown. We don't want the client to control the status bar, and
+            // we will dispatch the real visibility of status bar to the client.
             return null;
         }
         if (forceShowsStatusBarTransiently()) {
@@ -350,8 +379,7 @@
                 && !win.inMultiWindowMode();
     }
 
-    private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
-            boolean forceShowsSystemBarsForWindowingMode) {
+    private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
         final WindowState imeWin = mDisplayContent.mInputMethodWindow;
         if (imeWin != null && imeWin.isVisible()) {
             // Force showing navigation bar while IME is visible.
@@ -369,10 +397,9 @@
                     focusedWin.mAttrs.packageName);
             return mDisplayContent.mRemoteInsetsControlTarget;
         }
-        if (forceShowsSystemBarsForWindowingMode) {
-            // Navigation bar is forcibly shown for the windowing mode which is a steady state.
-            // We don't want the client to control the navigation bar, and we will dispatch the real
-            // visibility of navigation bar to the client.
+        if (mPolicy.areSystemBarsForcedShownLw()) {
+            // Navigation bar is forcibly shown. We don't want the client to control the navigation
+            // bar, and we will dispatch the real visibility of navigation bar to the client.
             return null;
         }
         if (forceShowsNavigationBarTransiently()) {
@@ -417,19 +444,6 @@
                 && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
     }
 
-    private boolean forceShowsSystemBarsForWindowingMode() {
-        final boolean isDockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        final boolean isFreeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
-                .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
-        final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
-
-        // We need to force system bars when the docked root task is visible, when the freeform
-        // root task is visible but also when we are resizing for the transitions when docked
-        // root task visibility changes.
-        return isDockedRootTaskVisible || isFreeformRootTaskVisible || isResizing;
-    }
-
     @VisibleForTesting
     void startAnimation(boolean show, Runnable callback) {
         int typesReady = 0;
@@ -474,8 +488,12 @@
             final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
             if (mState != state) {
                 mState = state;
-                mPolicy.getStatusBarManagerInternal().setWindowState(
-                        mDisplayContent.getDisplayId(), mId, state);
+                StatusBarManagerInternal statusBarManagerInternal =
+                        mPolicy.getStatusBarManagerInternal();
+                if (statusBarManagerInternal != null) {
+                    statusBarManagerInternal.setWindowState(
+                            mDisplayContent.getDisplayId(), mId, state);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7daebff..cbd1314 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -307,11 +307,6 @@
             // to control the window for now.
             return;
         }
-        if (target != null && target.getWindow() != null) {
-            // ime control target could be a different window.
-            // Refer WindowState#getImeControlTarget().
-            target = target.getWindow().getImeControlTarget();
-        }
 
         if (mWin != null && mWin.getSurfaceControl() == null) {
             // if window doesn't have a surface, set it null and return.
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3dbe79d..5a249a5 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -58,10 +58,12 @@
     private final LetterboxSurface mLeft = new LetterboxSurface("left");
     private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
     private final LetterboxSurface mRight = new LetterboxSurface("right");
-    // Prevents wallpaper from peeking through near rounded corners. It's not included in
-    // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
-    // or attachInput.
-    private final LetterboxSurface mBehind = new LetterboxSurface("behind");
+    // One surface that fills the whole window is used over multiple surfaces to:
+    // - Prevents wallpaper from peeking through near rounded corners.
+    // - For "blurred wallpaper" background, to avoid having visible border between surfaces.
+    // One surface approach isn't always preferred over multiple surfaces due to rendering cost
+    // for overlaping an app window and letterbox surfaces.
+    private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
     private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
 
     /**
@@ -104,7 +106,7 @@
         mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
         mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
         mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
-        mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
+        mFullWindowSurface.layout(outer.left, outer.top, outer.right, outer.bottom, surfaceOrigin);
     }
 
 
@@ -168,37 +170,46 @@
         for (LetterboxSurface surface : mSurfaces) {
             surface.remove();
         }
-        mBehind.remove();
+        mFullWindowSurface.remove();
     }
 
     /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
     public boolean needsApplySurfaceChanges() {
+        if (useFullWindowSurface()) {
+            return mFullWindowSurface.needsApplySurfaceChanges();
+        }
         for (LetterboxSurface surface : mSurfaces) {
             if (surface.needsApplySurfaceChanges()) {
                 return true;
             }
         }
-        if (mAreCornersRounded.get() && mBehind.needsApplySurfaceChanges()) {
-            return true;
-        }
         return false;
     }
 
     public void applySurfaceChanges(SurfaceControl.Transaction t) {
-        for (LetterboxSurface surface : mSurfaces) {
-            surface.applySurfaceChanges(t);
-        }
-        if (mAreCornersRounded.get()) {
-            mBehind.applySurfaceChanges(t);
+        if (useFullWindowSurface()) {
+            mFullWindowSurface.applySurfaceChanges(t);
+
+            for (LetterboxSurface surface : mSurfaces) {
+                surface.remove();
+            }
         } else {
-            mBehind.remove();
+            for (LetterboxSurface surface : mSurfaces) {
+                surface.applySurfaceChanges(t);
+            }
+
+            mFullWindowSurface.remove();
         }
     }
 
     /** Enables touches to slide into other neighboring surfaces. */
     void attachInput(WindowState win) {
-        for (LetterboxSurface surface : mSurfaces) {
-            surface.attachInput(win);
+        if (useFullWindowSurface()) {
+            mFullWindowSurface.attachInput(win);
+        } else {
+            for (LetterboxSurface surface : mSurfaces) {
+                surface.attachInput(win);
+            }
         }
     }
 
@@ -208,6 +219,16 @@
                 surface.mInputInterceptor.mWindowHandle.displayId = displayId;
             }
         }
+        if (mFullWindowSurface.mInputInterceptor != null) {
+            mFullWindowSurface.mInputInterceptor.mWindowHandle.displayId = displayId;
+        }
+    }
+
+    /**
+     * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
+     */
+    private boolean useFullWindowSurface() {
+        return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get();
     }
 
     private static class InputInterceptor {
@@ -308,6 +329,10 @@
             mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
         }
 
+        boolean isRemoved() {
+            return mSurface != null || mInputInterceptor != null;
+        }
+
         public void remove() {
             if (mSurface != null) {
                 mTransactionFactory.get().remove(mSurface).apply();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7174e68..eb7087c 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,7 +21,6 @@
 import android.graphics.Color;
 
 import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -105,12 +104,20 @@
      * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
      * the framework implementation will be used to determine the aspect ratio.
      */
-    @VisibleForTesting
     void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
         mFixedOrientationLetterboxAspectRatio = aspectRatio;
     }
 
     /**
+     * Resets the aspect ratio of letterbox for fixed orientation to {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
+     */
+    void resetFixedOrientationLetterboxAspectRatio() {
+        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
+    }
+
+    /**
      * Gets the aspect ratio of letterbox for fixed orientation.
      */
     float getFixedOrientationLetterboxAspectRatio() {
@@ -118,6 +125,25 @@
     }
 
     /**
+     * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+     * both it and a value of {@link
+     * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+     * and corners of the activity won't be rounded.
+     */
+    void setLetterboxActivityCornersRadius(int cornersRadius) {
+        mLetterboxActivityCornersRadius = cornersRadius;
+    }
+
+    /**
+     * Resets corners raidus for activities presented in the letterbox mode to {@link
+     * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+     */
+    void resetLetterboxActivityCornersRadius() {
+        mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+    }
+
+    /**
      * Whether corners of letterboxed activities are rounded.
      */
     boolean isLetterboxActivityCornersRounded() {
@@ -140,6 +166,25 @@
         return mLetterboxBackgroundColor;
     }
 
+
+    /**
+     * Sets color of letterbox background which is used when {@link
+     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+     * fallback for other backfround types.
+     */
+    void setLetterboxBackgroundColor(Color color) {
+        mLetterboxBackgroundColor = color;
+    }
+
+    /**
+     * Resets color of letterbox background to {@link
+     * com.android.internal.R.color.config_letterboxBackgroundColor}.
+     */
+    void resetLetterboxBackgroundColor() {
+        mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+                com.android.internal.R.color.config_letterboxBackgroundColor));
+    }
+
     /**
      * Gets {@link LetterboxBackgroundType} specified in {@link
      * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -149,6 +194,19 @@
         return mLetterboxBackgroundType;
     }
 
+    /** Sets letterbox background type. */
+    void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+        mLetterboxBackgroundType = backgroundType;
+    }
+
+    /**
+     * Resets cletterbox background type to {@link
+     * com.android.internal.R.integer.config_letterboxBackgroundType}.
+     */
+    void resetLetterboxBackgroundType() {
+        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+    }
+
     /** Returns a string representing the given {@link LetterboxBackgroundType}. */
     static String letterboxBackgroundTypeToString(
             @LetterboxBackgroundType int backgroundType) {
@@ -178,6 +236,27 @@
     }
 
     /**
+     * Overrides alpha of a black scrim shown over wallpaper for {@link
+     * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+     *
+     * <p>If given value is < 0 or >= 1, both it and a value of {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+     * and 0.0 (transparent) is instead.
+     */
+    void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+    }
+
+    /**
+     * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+     */
+    void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+    }
+
+    /**
      * Gets alpha of a black scrim shown over wallpaper letterbox background.
      */
     float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -185,6 +264,28 @@
     }
 
     /**
+     * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+     * {@link mLetterboxBackgroundType}.
+     *
+     * <p> If given value <= 0, both it and a value of {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+     * and 0 is used instead.
+     */
+    void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+        mLetterboxBackgroundWallpaperBlurRadius = radius;
+    }
+
+    /**
+     * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+     * mLetterboxBackgroundType} to {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+     */
+    void resetLetterboxBackgroundWallpaperBlurRadius() {
+        mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+    }
+
+    /**
      * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
      * mLetterboxBackgroundType}.
      */
@@ -211,9 +312,17 @@
      * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
      * central position (0.5) is used.
      */
-    @VisibleForTesting
     void setLetterboxHorizontalPositionMultiplier(float multiplier) {
         mLetterboxHorizontalPositionMultiplier = multiplier;
     }
 
+    /**
+     * Resets horizontal position of a center of the letterboxed app window to {@link
+     * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+     */
+    void resetLetterboxHorizontalPositionMultiplier() {
+        mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b1bdc11..ba1cf8a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -25,6 +25,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -123,6 +125,11 @@
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s",
                         targetActivity.getConfiguration());
             }
+        } else if (mDefaultTaskDisplayArea.getActivity(
+                ActivityRecord::occludesParent, false /* traverseTopToBottom */) == null) {
+            // Skip because none of above activities can occlude the target activity. The preload
+            // should be done silently in background without being visible.
+            return;
         } else {
             // Create the activity record. Because the activity is invisible, this doesn't really
             // start the client.
@@ -149,8 +156,7 @@
 
         // Invisible activity should be stopped. If the recents activity is alive and its doesn't
         // need to relaunch by current configuration, then it may be already in stopped state.
-        if (!targetActivity.isState(Task.ActivityState.STOPPING,
-                Task.ActivityState.STOPPED)) {
+        if (!targetActivity.isState(STOPPING, STOPPED)) {
             // Add to stopping instead of stop immediately. So the client has the chance to perform
             // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
             // things (e.g. the measure can be done earlier). The actual stop will be performed when
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b6e0d8d..629d20e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,10 +37,10 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -53,6 +53,11 @@
 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -70,14 +75,9 @@
 import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
 import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
 import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
 import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -509,6 +509,11 @@
         mTaskSupervisor.updateTopResumedActivityIfNeeded();
     }
 
+    @Override
+    boolean isAttached() {
+        return true;
+    }
+
     /**
      * Called when DisplayWindowSettings values may change.
      */
@@ -817,8 +822,7 @@
         }
 
         // Initialize state of exiting tokens.
-        final int numDisplays = mChildren.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+        for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             displayContent.setExitingTokensHasVisible(false);
         }
@@ -855,6 +859,7 @@
 
         // Send any pending task-info changes that were queued-up during a layout deferment
         mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+        mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
         mWmService.mSyncEngine.onSurfacePlacement();
         mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
 
@@ -867,7 +872,7 @@
             recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
         }
 
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+        for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             if (displayContent.mWallpaperMayChange) {
                 if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
@@ -929,12 +934,12 @@
         }
 
         // Time to remove any exiting tokens?
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+        for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             displayContent.removeExistingTokensIfPossible();
         }
 
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+        for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             if (displayContent.pendingLayoutChanges != 0) {
                 displayContent.setLayoutNeeded();
@@ -1865,7 +1870,7 @@
         if (focusedRootTask == null) {
             return null;
         }
-        final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+        final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
         if (resumedActivity != null && resumedActivity.app != null) {
             return resumedActivity;
         }
@@ -1887,11 +1892,11 @@
         // foreground.
         WindowProcessController fgApp = getItemFromRootTasks(rootTask -> {
             if (isTopDisplayFocusedRootTask(rootTask)) {
-                final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+                final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
                 if (resumedActivity != null) {
                     return resumedActivity.app;
-                } else if (rootTask.getPausingActivity() != null) {
-                    return rootTask.getPausingActivity().app;
+                } else if (rootTask.getTopPausingActivity() != null) {
+                    return rootTask.getTopPausingActivity().app;
                 }
             }
             return null;
@@ -1917,7 +1922,8 @@
                     return;
                 }
 
-                if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
+                if (rootTask.getVisibility(null /* starting */)
+                        == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
                     return;
                 }
 
@@ -2186,7 +2192,7 @@
                 // display area, so reparent.
                 rootTask.reparent(taskDisplayArea, true /* onTop */);
             }
-            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask);
+            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
 
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
@@ -2387,7 +2393,9 @@
                 if (displayShouldSleep) {
                     rootTask.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
-                    rootTask.awakeFromSleepingLocked();
+                    rootTask.forAllLeafTasksAndLeafTaskFragments(
+                            taskFragment -> taskFragment.awakeFromSleeping(),
+                            true /* traverseTopToBottom */);
                     if (rootTask.isFocusedRootTaskOnDisplay()
                             && !mTaskSupervisor.getKeyguardController()
                             .isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2764,8 +2772,8 @@
 
         if (DEBUG_SWITCH) {
             Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState()
-                    + " resumed=" + r.getTask().getResumedActivity() + " pausing="
-                    + r.getTask().getPausingActivity() + " for reason "
+                    + " resumed=" + r.getTask().getTopResumedActivity() + " pausing="
+                    + r.getTask().getTopPausingActivity() + " for reason "
                     + mDestroyAllActivitiesReason);
         }
 
@@ -3352,7 +3360,7 @@
             if (rootTask == null || !rootTask.hasActivity()) {
                 continue;
             }
-            final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+            final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
             if (resumedActivity == null || !resumedActivity.idle) {
                 ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
                         + "not idle", rootTask.getRootTaskId(), resumedActivity);
@@ -3367,7 +3375,7 @@
     boolean allResumedActivitiesVisible() {
         boolean[] foundResumed = {false};
         final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> {
-            final ActivityRecord r = rootTask.getResumedActivity();
+            final ActivityRecord r = rootTask.getTopResumedActivity();
             if (r != null) {
                 if (!r.nowVisible) {
                     return true;
@@ -3385,7 +3393,7 @@
     boolean allPausedActivitiesComplete() {
         boolean[] pausing = {true};
         final boolean hasActivityNotCompleted = forAllLeafTasks(task -> {
-            final ActivityRecord r = task.getPausingActivity();
+            final ActivityRecord r = task.getTopPausingActivity();
             if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
                 ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: "
                         + "r=%s state=%s", r, r.getState());
@@ -3428,14 +3436,6 @@
         }, true /* traverseTopToBottom */);
     }
 
-    void cancelInitializingActivities() {
-        forAllRootTasks(task -> {
-            // We don't want to clear starting window for activities that aren't occluded
-            // as we need to display their starting window until they are done initializing.
-            task.forAllOccludedActivities(ActivityRecord::cancelInitializing);
-        });
-    }
-
     Task anyTaskForId(int id) {
         return anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE);
     }
@@ -3515,11 +3515,6 @@
         return task;
     }
 
-    ActivityRecord isInAnyTask(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return (r != null && r.isDescendantOf(this)) ? r : null;
-    }
-
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
             int flags, int callingUid, ArraySet<Integer> profileIds) {
@@ -3560,6 +3555,8 @@
         }
     }
 
+    // TODO(b/191434136): handle this properly when we add multi-window support on secondary
+    //  display.
     private void calculateDefaultMinimalSizeOfResizeableTasks() {
         final Resources res = mService.mContext.getResources();
         final float minimalSize = res.getDimension(
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7ba772c..1533245 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -43,7 +43,11 @@
 
     // Comparator to sort by last active time (descending)
     private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR =
-            (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+            (o1, o2) -> {
+                return o1.lastActiveTime == o2.lastActiveTime
+                        ? Integer.signum(o2.mTaskId - o1.mTaskId) :
+                        Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+            };
 
     private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
 
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 4892005..2d4aef6 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -78,6 +78,18 @@
     }
 
     /**
+     * Constructs a new instance from a bundle and provided pid/uid.
+     *
+     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+     */
+    static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
+        return bOptions != null
+                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
+                        callingPid, callingUid)
+                : null;
+    }
+
+    /**
      * Constructs a new instance and records {@link Binder#getCallingPid}/
      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
      * this object.
@@ -91,6 +103,17 @@
     }
 
     /**
+     * Constructs a new instance.
+     *
+     * @param options The options to wrap.
+     */
+    private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
+        mOriginalCallingPid = callingPid;
+        mOriginalCallingUid = callingUid;
+        mOriginalOptions = options;
+    }
+
+    /**
      * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
      * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
      * method.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 382a2b1..5ffe7b6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,13 +27,10 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 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 android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
@@ -43,7 +40,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -53,9 +49,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -67,7 +60,6 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -84,22 +76,18 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 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.ActivityRecord.STARTING_WINDOW_SHOWN;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
 import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -112,7 +100,6 @@
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
@@ -123,21 +110,12 @@
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
 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;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
 import static com.android.server.wm.TaskProto.AFFINITY;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
 import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
 import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
 import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
 import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
 import static com.android.server.wm.TaskProto.RESIZE_MODE;
@@ -145,7 +123,7 @@
 import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
@@ -170,14 +148,8 @@
 import android.app.IActivityController;
 import android.app.PictureInPictureParams;
 import android.app.RemoteAction;
-import android.app.ResultInfo;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -244,23 +216,21 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-class Task extends WindowContainer<WindowContainer> {
+/**
+ * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
+ * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
+ * can also be an entity that showing in the Recents Screen for a job that user interacted with.
+ * A {@link Task} can also contain other {@link Task}s.
+ */
+class Task extends TaskFragment {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
-    static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-    private static final String TAG_APP = TAG + POSTFIX_APP;
     static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
-    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
-    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
-    private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
-    private static final String TAG_STATES = TAG + POSTFIX_STATES;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
     private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
@@ -303,10 +273,6 @@
     private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
     private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
 
-    // Set to false to disable the preview that is shown while a new activity
-    // is being started.
-    private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
     // How long to wait for all background Activities to redraw following a call to
     // convertToTranslucent().
     private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
@@ -315,7 +281,6 @@
     // code.
     static final int PERSIST_TASK_VERSION = 1;
 
-    static final int INVALID_MIN_SIZE = -1;
     private float mShadowRadius = 0;
 
     /**
@@ -335,36 +300,6 @@
     // Do not move the root task as a part of reparenting
     static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2;
 
-    @IntDef(prefix = {"TASK_VISIBILITY"}, value = {
-            TASK_VISIBILITY_VISIBLE,
-            TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
-            TASK_VISIBILITY_INVISIBLE,
-    })
-    @interface TaskVisibility {}
-
-    /** Task is visible. No other tasks on top that fully or partially occlude it. */
-    static final int TASK_VISIBILITY_VISIBLE = 0;
-
-    /** Task is partially occluded by other translucent task(s) on top of it. */
-    static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
-
-    /** Task is completely invisible. */
-    static final int TASK_VISIBILITY_INVISIBLE = 2;
-
-    enum ActivityState {
-        INITIALIZING,
-        STARTED,
-        RESUMED,
-        PAUSING,
-        PAUSED,
-        STOPPING,
-        STOPPED,
-        FINISHING,
-        DESTROYING,
-        DESTROYED,
-        RESTARTING_PROCESS
-    }
-
     // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
     // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
     // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
@@ -446,7 +381,6 @@
 
     CharSequence lastDescription; // Last description captured for this item.
 
-    Task mAdjacentTask; // Task adjacent to this one.
     int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
     Task mPrevAffiliate; // previous task in affiliated chain.
     int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -458,21 +392,12 @@
     String mCallingPackage;
     String mCallingFeatureId;
 
-    private final Rect mTmpStableBounds = new Rect();
-    private final Rect mTmpNonDecorBounds = new Rect();
-    private final Rect mTmpBounds = new Rect();
-    private final Rect mTmpInsets = new Rect();
-    private final Rect mTmpFullBounds = new Rect();
     private static final Rect sTmpBounds = new Rect();
 
     // Last non-fullscreen bounds the task was launched in or resized to.
     // The information is persisted and used to determine the appropriate root task to launch the
     // task into on restore.
     Rect mLastNonFullscreenBounds = null;
-    // Minimal width and height of this task when it's resizeable. -1 means it should use the
-    // default minimal width/height.
-    int mMinWidth;
-    int mMinHeight;
 
     // The surface transition of the target when recents animation is finished.
     // This is originally introduced to carry out the current surface control position and window
@@ -497,10 +422,6 @@
     /** Used by fillTaskInfo */
     final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport();
 
-    final ActivityTaskManagerService mAtmService;
-    final ActivityTaskSupervisor mTaskSupervisor;
-    final RootWindowContainer mRootWindowContainer;
-
     /* Unique identifier for this task. */
     final int mTaskId;
     /* User for which this task was created. */
@@ -571,29 +492,6 @@
     /** ActivityRecords that are exiting, but still on screen for animations. */
     final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
 
-    /**
-     * When we are in the process of pausing an activity, before starting the
-     * next one, this variable holds the activity that is currently being paused.
-     *
-     * Only set at leaf tasks.
-     */
-    @Nullable
-    private ActivityRecord mPausingActivity = null;
-
-    /**
-     * This is the last activity that we put into the paused state.  This is
-     * used to determine if we need to do an activity transition while sleeping,
-     * when we normally hold the top activity paused.
-     */
-    ActivityRecord mLastPausedActivity = null;
-
-    /**
-     * Current activity that is resumed, or null if there is none.
-     * Only set at leaf tasks.
-     */
-    @Nullable
-    private ActivityRecord mResumedActivity = null;
-
     private boolean mForceShowForAllUsers;
 
     /** When set, will force the task to report as invisible. */
@@ -641,121 +539,6 @@
     }
 
     private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
-    private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
-            new EnsureActivitiesVisibleHelper(this);
-    private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
-            new EnsureVisibleActivitiesConfigHelper();
-    private class EnsureVisibleActivitiesConfigHelper {
-        private boolean mUpdateConfig;
-        private boolean mPreserveWindow;
-        private boolean mBehindFullscreen;
-
-        void reset(boolean preserveWindow) {
-            mPreserveWindow = preserveWindow;
-            mUpdateConfig = false;
-            mBehindFullscreen = false;
-        }
-
-        void process(ActivityRecord start, boolean preserveWindow) {
-            if (start == null || !start.mVisibleRequested) {
-                return;
-            }
-            reset(preserveWindow);
-
-            final PooledFunction f = PooledLambda.obtainFunction(
-                    EnsureVisibleActivitiesConfigHelper::processActivity, this,
-                    PooledLambda.__(ActivityRecord.class));
-            forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
-            f.recycle();
-
-            if (mUpdateConfig) {
-                // Ensure the resumed state of the focus activity if we updated the configuration of
-                // any activity.
-                mRootWindowContainer.resumeFocusedTasksTopActivities();
-            }
-        }
-
-        boolean processActivity(ActivityRecord r) {
-            mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
-            mBehindFullscreen |= r.occludesParent();
-            return mBehindFullscreen;
-        }
-    }
-
-    private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
-            new CheckBehindFullscreenActivityHelper();
-    private class CheckBehindFullscreenActivityHelper {
-        private boolean mAboveTop;
-        private boolean mBehindFullscreenActivity;
-        private ActivityRecord mToCheck;
-        private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
-        private boolean mHandlingOccluded;
-
-        private void reset(ActivityRecord toCheck,
-                Consumer<ActivityRecord> handleBehindFullscreenActivity) {
-            mToCheck = toCheck;
-            mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
-            mAboveTop = true;
-            mBehindFullscreenActivity = false;
-
-            if (!shouldBeVisible(null)) {
-                // The root task is not visible, so no activity in it should be displaying a
-                // starting window. Mark all activities below top and behind fullscreen.
-                mAboveTop = false;
-                mBehindFullscreenActivity = true;
-            }
-
-            mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
-        }
-
-        boolean process(ActivityRecord toCheck,
-                Consumer<ActivityRecord> handleBehindFullscreenActivity) {
-            reset(toCheck, handleBehindFullscreenActivity);
-
-            if (!mHandlingOccluded && mBehindFullscreenActivity) {
-                return true;
-            }
-
-            final ActivityRecord topActivity = topRunningActivity();
-            final PooledFunction f = PooledLambda.obtainFunction(
-                    CheckBehindFullscreenActivityHelper::processActivity, this,
-                    PooledLambda.__(ActivityRecord.class), topActivity);
-            forAllActivities(f);
-            f.recycle();
-
-            return mBehindFullscreenActivity;
-        }
-
-        /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
-        private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
-            if (mAboveTop) {
-                if (r == topActivity) {
-                    if (r == mToCheck) {
-                        // It is the top activity in a visible root task.
-                        mBehindFullscreenActivity = false;
-                        return true;
-                    }
-                    mAboveTop = false;
-                }
-                mBehindFullscreenActivity |= r.occludesParent();
-                return false;
-            }
-
-            if (mHandlingOccluded) {
-                // Iterating through all occluded activities.
-                if (mBehindFullscreenActivity) {
-                    mHandleBehindFullscreenActivity.accept(r);
-                }
-            } else if (r == mToCheck) {
-                return true;
-            } else if (mBehindFullscreenActivity) {
-                // It is occluded before {@param toCheck} is found.
-                return true;
-            }
-            mBehindFullscreenActivity |= r.occludesParent();
-            return false;
-        }
-    }
 
     private final FindRootHelper mFindRootHelper = new FindRootHelper();
     private class FindRootHelper {
@@ -819,18 +602,6 @@
      */
     private boolean mForceNotOrganized;
 
-    /**
-     * This task was created by the task organizer which has the following implementations.
-     * <ul>
-     *     <lis>The task won't be removed when it is empty. Removal has to be an explicit request
-     *     from the task organizer.</li>
-     *     <li>Unlike other non-root tasks, it's direct children are visible to the task
-     *     organizer for ordering purposes.</li>
-     * </ul>
-     */
-    @VisibleForTesting
-    boolean mCreatedByOrganizer;
-
     // Tracking cookie for the creation of this task.
     IBinder mLaunchCookie;
 
@@ -858,11 +629,8 @@
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
             boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
             boolean _removeWithTaskOrganizer) {
-        super(atmService.mWindowManager);
+        super(atmService, null /* fragmentToken */, _createdByOrganizer);
 
-        mAtmService = atmService;
-        mTaskSupervisor = atmService.mTaskSupervisor;
-        mRootWindowContainer = mAtmService.mRootWindowContainer;
         mTaskId = _taskId;
         mUserId = _userId;
         mResizeMode = resizeMode;
@@ -875,7 +643,6 @@
                 : new PersistedTaskSnapshotData();
         // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
         setOrientation(SCREEN_ORIENTATION_UNSET);
-        mRemoteToken = new RemoteToken(this);
         affinityIntent = _affinityIntent;
         affinity = _affinity;
         rootAffinity = _rootAffinity;
@@ -913,7 +680,6 @@
         mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
         mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
 
-        mCreatedByOrganizer = _createdByOrganizer;
         mLaunchCookie = _launchCookie;
         mDeferTaskAppear = _deferTaskAppear;
         mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
@@ -1442,6 +1208,12 @@
         }
 
         if (newParent != null) {
+            // Surface of Task that will not be organized should be shown by default.
+            // See Task#showSurfaceOnCreation
+            if (!mCreatedByOrganizer && !canBeOrganized()) {
+                getSyncTransaction().show(mSurfaceControl);
+            }
+
             // TODO: Ensure that this is actually necessary here
             // Notify the voice session if required
             if (voiceSession != null) {
@@ -1466,64 +1238,80 @@
         mRootWindowContainer.updateUIDsPresentOnDisplay();
     }
 
-    void cleanUpActivityReferences(ActivityRecord r) {
-        // mPausingActivity is set at leaf task
-        if (mPausingActivity != null && mPausingActivity == r) {
-            mPausingActivity = null;
+    /** Returns the currently topmost resumed activity. */
+    @Nullable
+    ActivityRecord getTopResumedActivity() {
+        if (!isLeafTask()) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity();
+                if (resumedActivity != null) {
+                    return resumedActivity;
+                }
+            }
         }
 
-        if (mResumedActivity != null && mResumedActivity == r) {
-            setResumedActivity(null, "cleanUpActivityReferences");
+        final ActivityRecord taskResumedActivity = getResumedActivity();
+        ActivityRecord topResumedActivity = null;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (child.asTaskFragment() != null) {
+                final ActivityRecord[] resumedActivity = new ActivityRecord[1];
+                child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+                    if (fragment.getResumedActivity() != null) {
+                        resumedActivity[0] = fragment.getResumedActivity();
+                        return true;
+                    }
+                    return false;
+                });
+                topResumedActivity = resumedActivity[0];
+            } else if (taskResumedActivity != null
+                    && child.asActivityRecord() == taskResumedActivity) {
+                topResumedActivity = taskResumedActivity;
+            }
+            if (topResumedActivity != null) {
+                return topResumedActivity;
+            }
         }
-
-        final WindowContainer parent = getParent();
-        if (parent != null && parent.asTask() != null) {
-            parent.asTask().cleanUpActivityReferences(r);
-            return;
-        }
-        r.removeTimeouts();
-        mExitingActivities.remove(r);
-    }
-
-    /** @return the currently resumed activity. */
-    ActivityRecord getResumedActivity() {
-        if (isLeafTask()) {
-            return mResumedActivity;
-        }
-
-        final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */);
-        return task != null ? task.mResumedActivity : null;
-    }
-
-    @VisibleForTesting
-    void setPausingActivity(ActivityRecord pausing) {
-        mPausingActivity = pausing;
+        return null;
     }
 
     /**
-     * @return the currently pausing activity of this task or the topmost pausing activity of the
-     * child tasks
+     * Returns the currently topmost pausing activity.
      */
-    ActivityRecord getPausingActivity() {
-        if (isLeafTask()) {
-            return mPausingActivity;
+    @Nullable
+    ActivityRecord getTopPausingActivity() {
+        if (!isLeafTask()) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity();
+                if (pausingActivity != null) {
+                    return pausingActivity;
+                }
+            }
         }
 
-        final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */);
-        return task != null ? task.mPausingActivity : null;
-    }
-
-    void setResumedActivity(ActivityRecord r, String reason) {
-        warnForNonLeafTask("setResumedActivity");
-        if (mResumedActivity == r) {
-            return;
+        final ActivityRecord taskPausingActivity = getPausingActivity();
+        ActivityRecord topPausingActivity = null;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (child.asTaskFragment() != null) {
+                final ActivityRecord[] pausingActivity = new ActivityRecord[1];
+                child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+                    if (fragment.getPausingActivity() != null) {
+                        pausingActivity[0] = fragment.getPausingActivity();
+                        return true;
+                    }
+                    return false;
+                });
+                topPausingActivity = pausingActivity[0];
+            } else if (taskPausingActivity != null
+                    && child.asActivityRecord() == taskPausingActivity) {
+                topPausingActivity = taskPausingActivity;
+            }
+            if (topPausingActivity != null) {
+                return topPausingActivity;
+            }
         }
-
-        if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
-                "setResumedActivity task:" + this + " + from: "
-                + mResumedActivity + " to:" + r + " reason:" + reason);
-        mResumedActivity = r;
-        mTaskSupervisor.updateTopResumedActivityIfNeeded();
+        return null;
     }
 
     void updateTaskMovement(boolean toTop, int position) {
@@ -1562,11 +1350,6 @@
                 mTaskId, mUserId);
     }
 
-    void setAdjacentTask(Task adjacent) {
-        mAdjacentTask = adjacent;
-        adjacent.mAdjacentTask = this;
-    }
-
     void setTaskToAffiliateWith(Task taskToAffiliateWith) {
         closeRecentsChain();
         mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -1646,14 +1429,6 @@
                 window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
     }
 
-    ActivityRecord topActivityWithStartingWindow() {
-        if (getParent() == null) {
-            return null;
-        }
-        return getActivity((r) -> r.mStartingWindowState == STARTING_WINDOW_SHOWN
-                && r.okToShowLocked());
-    }
-
     /**
      * Return the number of running activities, and the number of non-finishing/initializing
      * activities in the provided {@param reportOut} respectively.
@@ -1675,22 +1450,7 @@
     }
 
     @Override
-    public int getActivityType() {
-        final int applicationType = super.getActivityType();
-        if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
-            return applicationType;
-        }
-        return getTopChild().getActivityType();
-    }
-
-    @Override
     void addChild(WindowContainer child, int index) {
-        // If this task had any child before we added this one.
-        boolean hadChild = hasChild();
-        // getActivityType() looks at the top child, so we need to read the type before adding
-        // a new child in case the new child is on top and UNDEFINED.
-        final int activityType = getActivityType();
-
         index = getAdjustedChildPosition(child, index);
         super.addChild(child, index);
 
@@ -1706,10 +1466,17 @@
         // now that this record is in a new task.
         mRootWindowContainer.updateUIDsPresentOnDisplay();
 
-        final ActivityRecord r = child.asActivityRecord();
-        if (r == null) return;
+        // Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be
+        // passed from Task constructor.
+        final TaskFragment childTaskFrag = child.asTaskFragment();
+        if (childTaskFrag != null && childTaskFrag.asTask() == null) {
+            childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);
+        }
+    }
 
-        r.inHistory = true;
+    /** Called when an {@link ActivityRecord} is added as a descendant */
+    void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) {
+        warnForNonLeafTask("onDescendantActivityAdded");
 
         // Only set this based on the first activity
         if (!hadChild) {
@@ -1736,10 +1503,6 @@
         updateEffectiveIntent();
     }
 
-    void addChild(ActivityRecord r) {
-        addChild(r, Integer.MAX_VALUE /* add on top */);
-    }
-
     @Override
     void removeChild(WindowContainer child) {
         removeChild(child, "removeChild");
@@ -1985,32 +1748,6 @@
                 && supportsMultiWindowInDisplayArea(tda);
     }
 
-    boolean supportsMultiWindow() {
-        return supportsMultiWindowInDisplayArea(getDisplayArea());
-    }
-
-    /**
-     * @return whether this task supports multi-window if it is in the given
-     *         {@link TaskDisplayArea}.
-     */
-    boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
-        if (!mAtmService.mSupportsMultiWindow) {
-            return false;
-        }
-        if (tda == null) {
-            Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi"
-                    + " window. Task id=" + mTaskId + " attached=" + isAttached());
-            return false;
-        }
-
-        if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) {
-            // Not support non-resizable in multi window.
-            return false;
-        }
-
-        return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
-    }
-
     /**
      * Check whether this task can be launched on the specified display.
      *
@@ -2146,60 +1883,6 @@
         }
     }
 
-    void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
-            @NonNull Configuration parentConfig) {
-        int minWidth = mMinWidth;
-        int minHeight = mMinHeight;
-        // If the task has no requested minimal size, we'd like to enforce a minimal size
-        // so that the user can not render the task too small to manipulate. We don't need
-        // to do this for the root pinned task as the bounds are controlled by the system.
-        if (!inPinnedWindowingMode()) {
-            final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
-            final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-            final int defaultMinSize = (int) (defaultMinSizeDp * density);
-
-            if (minWidth == INVALID_MIN_SIZE) {
-                minWidth = defaultMinSize;
-            }
-            if (minHeight == INVALID_MIN_SIZE) {
-                minHeight = defaultMinSize;
-            }
-        }
-        if (bounds.isEmpty()) {
-            // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
-            // do, we can just skip.
-            final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
-            if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
-                return;
-            }
-            bounds.set(parentBounds);
-        }
-        final boolean adjustWidth = minWidth > bounds.width();
-        final boolean adjustHeight = minHeight > bounds.height();
-        if (!(adjustWidth || adjustHeight)) {
-            return;
-        }
-
-        if (adjustWidth) {
-            if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
-                bounds.left = bounds.right - minWidth;
-            } else {
-                // Either left bounds match, or neither match, or the previous bounds were
-                // fullscreen and we default to keeping left.
-                bounds.right = bounds.left + minWidth;
-            }
-        }
-        if (adjustHeight) {
-            if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
-                bounds.top = bounds.bottom - minHeight;
-            } else {
-                // Either top bounds match, or neither match, or the previous bounds were
-                // fullscreen and we default to keeping top.
-                bounds.bottom = bounds.top + minHeight;
-            }
-        }
-    }
-
     void setLastNonFullscreenBounds(Rect bounds) {
         if (mLastNonFullscreenBounds == null) {
             mLastNonFullscreenBounds = new Rect(bounds);
@@ -2208,32 +1891,6 @@
         }
     }
 
-    /**
-     * This should be called when an child activity changes state. This should only
-     * be called from
-     * {@link ActivityRecord#setState(ActivityState, String)} .
-     * @param record The {@link ActivityRecord} whose state has changed.
-     * @param state The new state.
-     * @param reason The reason for the change.
-     */
-    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
-        warnForNonLeafTask("onActivityStateChanged");
-        if (record == mResumedActivity && state != RESUMED) {
-            setResumedActivity(null, reason + " - onActivityStateChanged");
-        }
-
-        if (state == RESUMED) {
-            if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
-                Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason);
-            }
-            setResumedActivity(record, reason + " - onActivityStateChanged");
-            if (record == mRootWindowContainer.getTopResumedActivity()) {
-                mAtmService.setResumedActivityUncheckLocked(record, reason);
-            }
-            mTaskSupervisor.mRecentTasks.add(record.getTask());
-        }
-    }
-
     private void onConfigurationChangedInner(Configuration newParentConfig) {
         // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
         // restore the last recorded non-fullscreen bounds.
@@ -2378,6 +2035,156 @@
         }
     }
 
+    void resolveLeafTaskOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) {
+        if (!isLeafTask()) {
+            return;
+        }
+
+        int windowingMode =
+                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+        }
+        // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+        // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+        getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+        Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // Use empty bounds to indicate "fill parent".
+            outOverrideBounds.setEmpty();
+            // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
+            // the parent or display is smaller than the size, the content may be cropped.
+            return;
+        }
+
+        adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            computeFreeformBounds(outOverrideBounds, newParentConfig);
+            return;
+        }
+    }
+
+    void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
+            @NonNull Configuration parentConfig) {
+        int minWidth = mMinWidth;
+        int minHeight = mMinHeight;
+        // If the task has no requested minimal size, we'd like to enforce a minimal size
+        // so that the user can not render the task fragment too small to manipulate. We don't need
+        // to do this for the root pinned task as the bounds are controlled by the system.
+        if (!inPinnedWindowingMode()) {
+            final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+            final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+            final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
+            if (minWidth == INVALID_MIN_SIZE) {
+                minWidth = defaultMinSize;
+            }
+            if (minHeight == INVALID_MIN_SIZE) {
+                minHeight = defaultMinSize;
+            }
+        }
+        if (bounds.isEmpty()) {
+            // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
+            // do, we can just skip.
+            final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+            if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
+                return;
+            }
+            bounds.set(parentBounds);
+        }
+        final boolean adjustWidth = minWidth > bounds.width();
+        final boolean adjustHeight = minHeight > bounds.height();
+        if (!(adjustWidth || adjustHeight)) {
+            return;
+        }
+
+        if (adjustWidth) {
+            if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
+                bounds.left = bounds.right - minWidth;
+            } else {
+                // Either left bounds match, or neither match, or the previous bounds were
+                // fullscreen and we default to keeping left.
+                bounds.right = bounds.left + minWidth;
+            }
+        }
+        if (adjustHeight) {
+            if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
+                bounds.top = bounds.bottom - minHeight;
+            } else {
+                // Either top bounds match, or neither match, or the previous bounds were
+                // fullscreen and we default to keeping top.
+                bounds.bottom = bounds.top + minHeight;
+            }
+        }
+    }
+
+    /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+    private void computeFreeformBounds(@NonNull Rect outBounds,
+            @NonNull Configuration newParentConfig) {
+        // by policy, make sure the window remains within parent somewhere
+        final float density =
+                ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+        final Rect parentBounds =
+                new Rect(newParentConfig.windowConfiguration.getBounds());
+        final DisplayContent display = getDisplayContent();
+        if (display != null) {
+            // If a freeform window moves below system bar, there is no way to move it again
+            // by touch. Because its caption is covered by system bar. So we exclude them
+            // from root task bounds. and then caption will be shown inside stable area.
+            final Rect stableBounds = new Rect();
+            display.getStableRect(stableBounds);
+            parentBounds.intersect(stableBounds);
+        }
+
+        fitWithinBounds(outBounds, parentBounds,
+                (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+                (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+        // Prevent to overlap caption with stable insets.
+        final int offsetTop = parentBounds.top - outBounds.top;
+        if (offsetTop > 0) {
+            outBounds.offset(0, offsetTop);
+        }
+    }
+
+    /**
+     * Adjusts bounds to stay within root task bounds.
+     *
+     * Since bounds might be outside of root task bounds, this method tries to move the bounds in
+     * a way that keep them unchanged, but be contained within the root task bounds.
+     *
+     * @param bounds Bounds to be adjusted.
+     * @param rootTaskBounds Bounds within which the other bounds should remain.
+     * @param overlapPxX The amount of px required to be visible in the X dimension.
+     * @param overlapPxY The amount of px required to be visible in the Y dimension.
+     */
+    private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
+            int overlapPxY) {
+        if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
+            return;
+        }
+
+        // For each side of the parent (eg. left), check if the opposing side of the window (eg.
+        // right) is at least overlap pixels away. If less, offset the window by that difference.
+        int horizontalDiff = 0;
+        // If window is smaller than overlap, use it's smallest dimension instead
+        int overlapLR = Math.min(overlapPxX, bounds.width());
+        if (bounds.right < (rootTaskBounds.left + overlapLR)) {
+            horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
+        } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
+            horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
+        }
+        int verticalDiff = 0;
+        int overlapTB = Math.min(overlapPxY, bounds.width());
+        if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
+            verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
+        } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
+            verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
+        }
+        bounds.offset(horizontalDiff, verticalDiff);
+    }
+
     /**
      * Initializes a change transition. See {@link SurfaceFreezer} for more information.
      */
@@ -2526,400 +2333,6 @@
         mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display);
     }
 
-    /**
-     * Adjust bounds to stay within root task bounds.
-     *
-     * Since bounds might be outside of root task bounds, this method tries to move the bounds in
-     * a way that keep them unchanged, but be contained within the root task bounds.
-     *
-     * @param bounds Bounds to be adjusted.
-     * @param rootTaskBounds Bounds within which the other bounds should remain.
-     * @param overlapPxX The amount of px required to be visible in the X dimension.
-     * @param overlapPxY The amount of px required to be visible in the Y dimension.
-     */
-    private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
-            int overlapPxY) {
-        if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
-            return;
-        }
-
-        // For each side of the parent (eg. left), check if the opposing side of the window (eg.
-        // right) is at least overlap pixels away. If less, offset the window by that difference.
-        int horizontalDiff = 0;
-        // If window is smaller than overlap, use it's smallest dimension instead
-        int overlapLR = Math.min(overlapPxX, bounds.width());
-        if (bounds.right < (rootTaskBounds.left + overlapLR)) {
-            horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
-        } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
-            horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
-        }
-        int verticalDiff = 0;
-        int overlapTB = Math.min(overlapPxY, bounds.width());
-        if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
-            verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
-        } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
-            verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
-        }
-        bounds.offset(horizontalDiff, verticalDiff);
-    }
-
-    /**
-     * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
-     * intersectBounds on a side, then the respective side will not be intersected.
-     *
-     * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
-     * inset on that side is no-longer applicable. This scenario happens when a task's minimal
-     * bounds are larger than the provided parent/display bounds.
-     *
-     * @param inOutBounds the bounds to intersect.
-     * @param intersectBounds the bounds to intersect with.
-     * @param intersectInsets insets to apply to intersectBounds before intersecting.
-     */
-    static void intersectWithInsetsIfFits(
-            Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
-        if (inOutBounds.right <= intersectBounds.right) {
-            inOutBounds.right =
-                    Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
-        }
-        if (inOutBounds.bottom <= intersectBounds.bottom) {
-            inOutBounds.bottom =
-                    Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
-        }
-        if (inOutBounds.left >= intersectBounds.left) {
-            inOutBounds.left =
-                    Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
-        }
-        if (inOutBounds.top >= intersectBounds.top) {
-            inOutBounds.top =
-                    Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
-        }
-    }
-
-    /**
-     * Gets bounds with non-decor and stable insets applied respectively.
-     *
-     * If bounds overhangs the display, those edges will not get insets. See
-     * {@link #intersectWithInsetsIfFits}
-     *
-     * @param outNonDecorBounds where to place bounds with non-decor insets applied.
-     * @param outStableBounds where to place bounds with stable insets applied.
-     * @param bounds the bounds to inset.
-     */
-    private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
-            DisplayInfo displayInfo) {
-        outNonDecorBounds.set(bounds);
-        outStableBounds.set(bounds);
-        final Task rootTask = getRootTask();
-        if (rootTask == null || rootTask.mDisplayContent == null) {
-            return;
-        }
-        mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-
-        final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
-        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
-                displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
-        intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
-        policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
-        intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
-    }
-
-    /**
-     * Forces the app bounds related configuration can be computed by
-     * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
-     * ActivityRecord.CompatDisplayInsets)}.
-     */
-    private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
-        final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
-        if (appBounds != null) {
-            appBounds.setEmpty();
-        }
-        inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-        inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-    }
-
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
-            @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
-        if (overrideDisplayInfo != null) {
-            // Make sure the screen related configs can be computed by the provided display info.
-            inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
-            invalidateAppBoundsConfig(inOutConfig);
-        }
-        computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
-                null /* compatInsets */);
-    }
-
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
-            @NonNull Configuration parentConfig) {
-        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
-                null /* compatInsets */);
-    }
-
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
-            @NonNull Configuration parentConfig,
-            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
-        if (compatInsets != null) {
-            // Make sure the app bounds can be computed by the compat insets.
-            invalidateAppBoundsConfig(inOutConfig);
-        }
-        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
-                compatInsets);
-    }
-
-    /**
-     * Calculates configuration values used by the client to get resources. This should be run
-     * using app-facing bounds (bounds unmodified by animations or transient interactions).
-     *
-     * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
-     * configuring an "inherit-bounds" window which means that all configuration settings would
-     * just be inherited from the parent configuration.
-     **/
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
-            @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
-            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
-        int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-            windowingMode = parentConfig.windowConfiguration.getWindowingMode();
-        }
-
-        float density = inOutConfig.densityDpi;
-        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
-            density = parentConfig.densityDpi;
-        }
-        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
-        // The bounds may have been overridden at this level. If the parent cannot cover these
-        // bounds, the configuration is still computed according to the override bounds.
-        final boolean insideParentBounds;
-
-        final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
-        final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
-        if (resolvedBounds == null || resolvedBounds.isEmpty()) {
-            mTmpFullBounds.set(parentBounds);
-            insideParentBounds = true;
-        } else {
-            mTmpFullBounds.set(resolvedBounds);
-            insideParentBounds = parentBounds.contains(resolvedBounds);
-        }
-
-        // Non-null compatibility insets means the activity prefers to keep its original size, so
-        // out bounds doesn't need to be restricted by the parent or current display
-        final boolean customContainerPolicy = compatInsets != null;
-
-        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-        if (outAppBounds == null || outAppBounds.isEmpty()) {
-            // App-bounds hasn't been overridden, so calculate a value for it.
-            inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
-            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-
-            if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
-                final Rect containingAppBounds;
-                if (insideParentBounds) {
-                    containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
-                } else {
-                    // Restrict appBounds to display non-decor rather than parent because the
-                    // override bounds are beyond the parent. Otherwise, it won't match the
-                    // overridden bounds.
-                    final TaskDisplayArea displayArea = getDisplayArea();
-                    containingAppBounds = displayArea != null
-                            ? displayArea.getWindowConfiguration().getAppBounds() : null;
-                }
-                if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
-                    outAppBounds.intersect(containingAppBounds);
-                }
-            }
-        }
-
-        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
-                || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
-                mTmpNonDecorBounds.set(mTmpFullBounds);
-                mTmpStableBounds.set(mTmpFullBounds);
-            } else if (!customContainerPolicy
-                    && (overrideDisplayInfo != null || getDisplayContent() != null)) {
-                final DisplayInfo di = overrideDisplayInfo != null
-                        ? overrideDisplayInfo
-                        : getDisplayContent().getDisplayInfo();
-
-                // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
-                // area, i.e. the screen area without the system bars.
-                // The non decor inset are areas that could never be removed in Honeycomb. See
-                // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
-                calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
-            } else {
-                // Apply the given non-decor and stable insets to calculate the corresponding bounds
-                // for screen size of configuration.
-                int rotation = inOutConfig.windowConfiguration.getRotation();
-                if (rotation == ROTATION_UNDEFINED) {
-                    rotation = parentConfig.windowConfiguration.getRotation();
-                }
-                if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
-                    mTmpNonDecorBounds.set(mTmpFullBounds);
-                    mTmpStableBounds.set(mTmpFullBounds);
-                    compatInsets.getBoundsByRotation(mTmpBounds, rotation);
-                    intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
-                            compatInsets.mNonDecorInsets[rotation]);
-                    intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
-                            compatInsets.mStableInsets[rotation]);
-                    outAppBounds.set(mTmpNonDecorBounds);
-                } else {
-                    // Set to app bounds because it excludes decor insets.
-                    mTmpNonDecorBounds.set(outAppBounds);
-                    mTmpStableBounds.set(outAppBounds);
-                }
-            }
-
-            if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-                final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
-                inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
-                        ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
-                        : overrideScreenWidthDp;
-            }
-            if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-                final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
-                inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
-                        ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
-                        : overrideScreenHeightDp;
-            }
-
-            if (inOutConfig.smallestScreenWidthDp
-                    == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
-                if (WindowConfiguration.isFloating(windowingMode)) {
-                    // For floating tasks, calculate the smallest width from the bounds of the task
-                    inOutConfig.smallestScreenWidthDp = (int) (
-                            Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
-                }
-                // otherwise, it will just inherit
-            }
-        }
-
-        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
-            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
-                    ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        }
-        if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
-            // For calculating screen layout, we need to use the non-decor inset screen area for the
-            // calculation for compatibility reasons, i.e. screen area without system bars that
-            // could never go away in Honeycomb.
-            int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
-            int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
-            // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
-            // undefined so it can't be used.
-            if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-                compatScreenWidthDp = inOutConfig.screenWidthDp;
-            }
-            if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-                compatScreenHeightDp = inOutConfig.screenHeightDp;
-            }
-            // Reducing the screen layout starting from its parent config.
-            inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
-                    compatScreenWidthDp, compatScreenHeightDp);
-        }
-    }
-
-    /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
-    static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
-            int screenHeightDp) {
-        sourceScreenLayout = sourceScreenLayout
-                & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
-        final int longSize = Math.max(screenWidthDp, screenHeightDp);
-        final int shortSize = Math.min(screenWidthDp, screenHeightDp);
-        return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
-    }
-
-    @Override
-    void resolveOverrideConfiguration(Configuration newParentConfig) {
-        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
-        super.resolveOverrideConfiguration(newParentConfig);
-
-        int windowingMode =
-                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
-        final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
-
-        // Resolve override windowing mode to fullscreen for home task (even on freeform
-        // display), or split-screen if in split-screen mode.
-        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
-            windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
-                    ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
-            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
-        }
-
-        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
-        // pinned windowing mode.
-        if (!supportsMultiWindow()) {
-            final int candidateWindowingMode =
-                    windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
-            if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
-                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
-                getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
-                        WINDOWING_MODE_FULLSCREEN);
-            }
-        }
-
-        if (isLeafTask()) {
-            resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
-        }
-        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
-    }
-
-    private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
-            Rect previousBounds) {
-
-        int windowingMode =
-                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
-        }
-        // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
-        // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
-        getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
-        Rect outOverrideBounds =
-                getResolvedOverrideConfiguration().windowConfiguration.getBounds();
-
-        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
-            // Use empty bounds to indicate "fill parent".
-            outOverrideBounds.setEmpty();
-            // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
-            // the parent or display is smaller than the size, the content may be cropped.
-            return;
-        }
-
-        adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
-        if (windowingMode == WINDOWING_MODE_FREEFORM) {
-            computeFreeformBounds(outOverrideBounds, newParentConfig);
-            return;
-        }
-    }
-
-    /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
-    private void computeFreeformBounds(@NonNull Rect outBounds,
-            @NonNull Configuration newParentConfig) {
-        // by policy, make sure the window remains within parent somewhere
-        final float density =
-                ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
-        final Rect parentBounds =
-                new Rect(newParentConfig.windowConfiguration.getBounds());
-        final DisplayContent display = getDisplayContent();
-        if (display != null) {
-            // If a freeform window moves below system bar, there is no way to move it again
-            // by touch. Because its caption is covered by system bar. So we exclude them
-            // from root task bounds. and then caption will be shown inside stable area.
-            final Rect stableBounds = new Rect();
-            display.getStableRect(stableBounds);
-            parentBounds.intersect(stableBounds);
-        }
-
-        fitWithinBounds(outBounds, parentBounds,
-                (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
-                (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
-
-        // Prevent to overlap caption with stable insets.
-        final int offsetTop = parentBounds.top - outBounds.top;
-        if (offsetTop > 0) {
-            outBounds.offset(0, offsetTop);
-        }
-    }
-
     Rect updateOverrideConfigurationFromLaunchBounds() {
         // If the task is controlled by another organized task, do not set override
         // configurations and let its parent (organized task) to control it;
@@ -2968,22 +2381,14 @@
         }
     }
 
-    int getDisplayId() {
-        final DisplayContent dc = getDisplayContent();
-        return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
-    }
-
     /** @return Id of root task. */
     int getRootTaskId() {
         return getRootTask().mTaskId;
     }
 
+    @Nullable
     Task getRootTask() {
-        final WindowContainer parent = getParent();
-        if (parent == null) return this;
-
-        final Task parentTask = parent.asTask();
-        return parentTask == null ? this : parentTask.getRootTask();
+        return getRootTaskFragment().asTask();
     }
 
     /** @return the first organized task. */
@@ -3098,12 +2503,12 @@
         // and focused application if needed.
         focusableTask.moveToFront(myReason);
         // Top display focused root task is changed, update top resumed activity if needed.
-        if (rootTask.getResumedActivity() != null) {
+        if (rootTask.getTopResumedActivity() != null) {
             mTaskSupervisor.updateTopResumedActivityIfNeeded();
             // Set focused app directly because if the next focused activity is already resumed
             // (e.g. the next top activity is on a different display), there won't have activity
             // state change to update it.
-            mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason);
+            mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason);
         }
         return rootTask;
     }
@@ -3152,17 +2557,16 @@
 
         // Figure-out min/max possible position depending on if child can show for current user.
         int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0;
-        int maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
-        if (!hasChild(wc)) {
-            // Increase the maxPosition because children size will grow once wc is added.
-            ++maxPosition;
+        int maxPosition = minPosition;
+        if (size > 0) {
+            maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
         }
 
         // Factor in always-on-top children in max possible position.
         if (!wc.isAlwaysOnTop()) {
             // We want to place all non-always-on-top containers below always-on-top ones.
             while (maxPosition > minPosition) {
-                if (!mChildren.get(maxPosition - 1).isAlwaysOnTop()) break;
+                if (!mChildren.get(maxPosition).isAlwaysOnTop()) break;
                 --maxPosition;
             }
         }
@@ -3173,6 +2577,12 @@
         } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
             return POSITION_TOP;
         }
+
+        // Increase the maxPosition because children size will grow once wc is added.
+        if (!hasChild(wc)) {
+            ++maxPosition;
+        }
+
         // Reset position based on minimum/maximum possible positions.
         return Math.min(Math.max(suggestedPosition, minPosition), maxPosition);
     }
@@ -3554,18 +2964,6 @@
         mForceShowForAllUsers = forceShowForAllUsers;
     }
 
-    @Override
-    public boolean isAttached() {
-        final TaskDisplayArea taskDisplayArea = getDisplayArea();
-        return taskDisplayArea != null && !taskDisplayArea.isRemoved();
-    }
-
-    @Override
-    @Nullable
-    TaskDisplayArea getDisplayArea() {
-        return (TaskDisplayArea) super.getDisplayArea();
-    }
-
     /**
      * When we are in a floating root task (Freeform, Pinned, ...) we calculate
      * insets differently. However if we are animating to the fullscreen root task
@@ -3576,45 +2974,6 @@
         return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
     }
 
-    /**
-     * Returns true if the root task is translucent and can have other contents visible behind it if
-     * needed. A root task is considered translucent if it don't contain a visible or
-     * starting (about to be visible) activity that is fullscreen (opaque).
-     * @param starting The currently starting activity or null if there is none.
-     */
-    @VisibleForTesting
-    boolean isTranslucent(ActivityRecord starting) {
-        if (!isAttached() || isForceHidden()) {
-            return true;
-        }
-        final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
-                PooledLambda.__(ActivityRecord.class), starting);
-        final ActivityRecord opaque = getActivity(p);
-        p.recycle();
-        return opaque == null;
-    }
-
-    private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
-        if (r.finishing) {
-            // We don't factor in finishing activities when determining translucency since
-            // they will be gone soon.
-            return false;
-        }
-
-        if (!r.visibleIgnoringKeyguard && r != starting) {
-            // Also ignore invisible activities that are not the currently starting
-            // activity (about to be visible).
-            return false;
-        }
-
-        if (r.occludesParent()) {
-            // Root task isn't translucent if it has at least one fullscreen activity
-            // that is visible.
-            return true;
-        }
-        return false;
-    }
-
     /** Returns the top-most activity that occludes the given one, or {@code null} if none. */
     @Nullable
     ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
@@ -3623,25 +2982,6 @@
         return top != activity ? top : null;
     }
 
-    /** Iterates through all occluded activities. */
-    void forAllOccludedActivities(Consumer<ActivityRecord> handleOccludedActivity) {
-        if (!shouldBeVisible(null /* starting */)) {
-            // The root task is invisible so all activities are occluded.
-            forAllActivities(handleOccludedActivity);
-            return;
-        }
-        final ActivityRecord topOccluding = getOccludingActivityAbove(null);
-        if (topOccluding == null) {
-            // No activities are occluded.
-            return;
-        }
-        // Invoke the callback on the activities behind the top occluding activity.
-        forAllActivities(r -> {
-            handleOccludedActivity.accept(r);
-            return false;
-        }, topOccluding, false /* includeBoundary */, true /* traverseTopToBottom */);
-    }
-
     @Override
     public SurfaceControl.Builder makeAnimationLeash() {
         return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
@@ -3708,19 +3048,6 @@
         return activity != null ? activity.findMainWindow() : null;
     }
 
-    ActivityRecord topRunningActivity() {
-        return topRunningActivity(false /* focusableOnly */);
-    }
-
-    ActivityRecord topRunningActivity(boolean focusableOnly) {
-        // Split into 2 to avoid object creation due to variable capture.
-        if (focusableOnly) {
-            return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
-        } else {
-            return getActivity(ActivityRecord::canBeTopRunning);
-        }
-    }
-
     ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
         final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunningNonDelayed
                 , PooledLambda.__(ActivityRecord.class), notTop);
@@ -3775,12 +3102,6 @@
         });
     }
 
-    boolean isTopActivityFocusable() {
-        final ActivityRecord r = topRunningActivity();
-        return r != null ? r.isFocusable()
-                : (isFocusable() && getWindowConfiguration().canReceiveKeys());
-    }
-
     boolean isFocusableAndVisible() {
         return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
     }
@@ -3896,6 +3217,41 @@
         return false;
     }
 
+    /** Iterates through all leaf task fragments and the leaf tasks. */
+    void forAllLeafTasksAndLeafTaskFragments(final Consumer<TaskFragment> callback,
+            boolean traverseTopToBottom) {
+        forAllLeafTasks(task -> {
+            if (task.isLeafTaskFragment()) {
+                callback.accept(task);
+                return;
+            }
+
+            // A leaf task that may contains both activities and task fragments.
+            boolean consumed = false;
+            if (traverseTopToBottom) {
+                for (int i = task.mChildren.size() - 1; i >= 0; --i) {
+                    final WindowContainer child = mChildren.get(i);
+                    if (child.asTaskFragment() != null) {
+                        child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+                    } else if (child.asActivityRecord() != null && !consumed) {
+                        callback.accept(task);
+                        consumed = true;
+                    }
+                }
+            } else {
+                for (int i = 0; i < task.mChildren.size(); i++) {
+                    final WindowContainer child = mChildren.get(i);
+                    if (child.asTaskFragment() != null) {
+                        child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+                    } else if (child.asActivityRecord() != null && !consumed) {
+                        callback.accept(task);
+                        consumed = true;
+                    }
+                }
+            }
+        }, traverseTopToBottom);
+    }
+
     @Override
     boolean forAllRootTasks(Function<Task, Boolean> callback, boolean traverseTopToBottom) {
         return isRootTask() ? callback.apply(this) : false;
@@ -4066,6 +3422,9 @@
         info.userId = isLeafTask() ? mUserId : mCurrentUser;
         info.taskId = mTaskId;
         info.displayId = getDisplayId();
+        if (tda != null) {
+            info.displayAreaFeatureId = tda.mFeatureId;
+        }
         info.isRunning = getTopNonFinishingActivity() != null;
         final Intent baseIntent = getBaseIntent();
         // Make a copy of base intent because this is like a snapshot info.
@@ -4125,6 +3484,7 @@
                 : INVALID_TASK_ID;
         info.isFocused = isFocused();
         info.isVisible = hasVisibleChildren();
+        info.isSleeping = shouldSleepActivities();
         ActivityRecord topRecord = getTopNonFinishingActivity();
         info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
     }
@@ -4135,9 +3495,9 @@
 
     private @Nullable PictureInPictureParams getPictureInPictureParams(Task top) {
         if (top == null) return null;
-        final ActivityRecord topVisibleActivity = top.getTopVisibleActivity();
-        return (topVisibleActivity == null || topVisibleActivity.pictureInPictureArgs.empty())
-                ? null : new PictureInPictureParams(topVisibleActivity.pictureInPictureArgs);
+        final ActivityRecord topMostActivity = top.getTopMostActivity();
+        return (topMostActivity == null || topMostActivity.pictureInPictureArgs.empty())
+                ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
     }
 
     Rect getDisplayCutoutInsets() {
@@ -4202,184 +3562,6 @@
         return this;
     }
 
-    /**
-     * Returns true if the task should be visible.
-     *
-     * @param starting The currently starting activity or null if there is none.
-     */
-    boolean shouldBeVisible(ActivityRecord starting) {
-        return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE;
-    }
-
-    /**
-     * Returns true if the task should be visible.
-     *
-     * @param starting The currently starting activity or null if there is none.
-     */
-    @TaskVisibility
-    int getVisibility(ActivityRecord starting) {
-        if (!isAttached() || isForceHidden()) {
-            return TASK_VISIBILITY_INVISIBLE;
-        }
-
-        if (isTopActivityLaunchedBehind()) {
-            return TASK_VISIBILITY_VISIBLE;
-        }
-
-        boolean gotRootSplitScreenTask = false;
-        boolean gotOpaqueSplitScreenPrimary = false;
-        boolean gotOpaqueSplitScreenSecondary = false;
-        boolean gotTranslucentFullscreen = false;
-        boolean gotTranslucentSplitScreenPrimary = false;
-        boolean gotTranslucentSplitScreenSecondary = false;
-        boolean shouldBeVisible = true;
-
-        // This root task is only considered visible if all its parent root tasks are considered
-        // visible, so check the visibility of all ancestor root task first.
-        final WindowContainer parent = getParent();
-        if (parent.asTask() != null) {
-            final int parentVisibility = parent.asTask().getVisibility(starting);
-            if (parentVisibility == TASK_VISIBILITY_INVISIBLE) {
-                // Can't be visible if parent isn't visible
-                return TASK_VISIBILITY_INVISIBLE;
-            } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
-                // Parent is behind a translucent container so the highest visibility this container
-                // can get is that.
-                gotTranslucentFullscreen = true;
-            }
-        }
-
-        final List<Task> adjacentTasks = new ArrayList<>();
-        final int windowingMode = getWindowingMode();
-        final boolean isAssistantType = isActivityTypeAssistant();
-        for (int i = parent.getChildCount() - 1; i >= 0; --i) {
-            final WindowContainer wc = parent.getChildAt(i);
-            final Task other = wc.asTask();
-            if (other == null) continue;
-
-            final boolean hasRunningActivities = other.topRunningActivity() != null;
-            if (other == this) {
-                // Should be visible if there is no other stack occluding it, unless it doesn't
-                // have any running activities, not starting one and not home stack.
-                shouldBeVisible = hasRunningActivities || isInTask(starting) != null
-                        || isActivityTypeHome();
-                break;
-            }
-
-            if (!hasRunningActivities) {
-                continue;
-            }
-
-            final int otherWindowingMode = other.getWindowingMode();
-
-            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
-                if (other.isTranslucent(starting)) {
-                    // Can be visible behind a translucent fullscreen stack.
-                    gotTranslucentFullscreen = true;
-                    continue;
-                }
-                return TASK_VISIBILITY_INVISIBLE;
-            } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
-                    && other.matchParentBounds()) {
-                if (other.isTranslucent(starting)) {
-                    // Can be visible behind a translucent task.
-                    gotTranslucentFullscreen = true;
-                    continue;
-                }
-                // Multi-window task that matches parent bounds would occlude other children.
-                return TASK_VISIBILITY_INVISIBLE;
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    && !gotOpaqueSplitScreenPrimary) {
-                gotRootSplitScreenTask = true;
-                gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
-                gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                        && gotOpaqueSplitScreenPrimary) {
-                    // Can not be visible behind another opaque stack in split-screen-primary mode.
-                    return TASK_VISIBILITY_INVISIBLE;
-                }
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    && !gotOpaqueSplitScreenSecondary) {
-                gotRootSplitScreenTask = true;
-                gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
-                gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        && gotOpaqueSplitScreenSecondary) {
-                    // Can not be visible behind another opaque stack in split-screen-secondary mode.
-                    return TASK_VISIBILITY_INVISIBLE;
-                }
-            }
-            if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
-                // Can not be visible if we are in split-screen windowing mode and both halves of
-                // the screen are opaque.
-                return TASK_VISIBILITY_INVISIBLE;
-            }
-            if (isAssistantType && gotRootSplitScreenTask) {
-                // Assistant stack can't be visible behind split-screen. In addition to this not
-                // making sense, it also works around an issue here we boost the z-order of the
-                // assistant window surfaces in window manager whenever it is visible.
-                return TASK_VISIBILITY_INVISIBLE;
-            }
-            if (other.mAdjacentTask != null) {
-                if (adjacentTasks.contains(other.mAdjacentTask)) {
-                    if (other.isTranslucent(starting)
-                            || other.mAdjacentTask.isTranslucent(starting)) {
-                        // Can be visible behind a translucent adjacent tasks.
-                        gotTranslucentFullscreen = true;
-                        continue;
-                    }
-                    // Can not be visible behind adjacent tasks.
-                    return TASK_VISIBILITY_INVISIBLE;
-                } else {
-                    adjacentTasks.add(other);
-                }
-            }
-        }
-
-        if (!shouldBeVisible) {
-            return TASK_VISIBILITY_INVISIBLE;
-        }
-
-        // Handle cases when there can be a translucent split-screen stack on top.
-        switch (windowingMode) {
-            case WINDOWING_MODE_FULLSCREEN:
-                if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
-                    // At least one of the split-screen stacks that covers this one is translucent.
-                    // When in split mode, home task will be reparented to the secondary split while
-                    // leaving tasks not supporting split below. Due to
-                    // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
-                    // the bottom, this makes sure tasks not in split roots won't occlude home task
-                    // unexpectedly.
-                    return TASK_VISIBILITY_INVISIBLE;
-                }
-                break;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (gotTranslucentSplitScreenPrimary) {
-                    // Covered by translucent primary split-screen on top.
-                    return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-                }
-                break;
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                if (gotTranslucentSplitScreenSecondary) {
-                    // Covered by translucent secondary split-screen on top.
-                    return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-                }
-                break;
-        }
-
-        // Lastly - check if there is a translucent fullscreen stack on top.
-        return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
-                : TASK_VISIBILITY_VISIBLE;
-    }
-
-    private boolean isTopActivityLaunchedBehind() {
-        final ActivityRecord top = topRunningActivity();
-        if (top != null && top.mLaunchTaskBehind) {
-            return true;
-        }
-        return false;
-    }
-
     ActivityRecord isInTask(ActivityRecord r) {
         if (r == null) {
             return null;
@@ -4509,6 +3691,8 @@
             }
             sb.append(" visible=");
             sb.append(shouldBeVisible(null /* starting */));
+            sb.append(" visibleRequested=");
+            sb.append(isVisibleRequested());
             sb.append(" mode=");
             sb.append(windowingModeToString(getWindowingMode()));
             sb.append(" translucent=");
@@ -4562,7 +3746,7 @@
             // Increment the total number of non-finishing activities
             numActivities++;
 
-            if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+            if (top == null || (top.isState(INITIALIZING))) {
                 top = r;
                 // Reset the number of running activities until we hit the first non-initializing
                 // activity
@@ -5305,9 +4489,7 @@
         return super.isAlwaysOnTop();
     }
 
-    /**
-     * Returns whether this task is currently forced to be hidden for any reason.
-     */
+    @Override
     protected boolean isForceHidden() {
         return mForceHiddenFlags != 0;
     }
@@ -5593,19 +4775,6 @@
         r.completeResumeLocked();
     }
 
-    void awakeFromSleepingLocked() {
-        if (!isLeafTask()) {
-            forAllLeafTasks((task) -> task.awakeFromSleepingLocked(),
-                    true /* traverseTopToBottom */);
-            return;
-        }
-
-        if (mPausingActivity != null) {
-            Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
-            mPausingActivity.activityPaused(true);
-        }
-    }
-
     void checkReadyForSleep() {
         if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
             mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -5624,302 +4793,13 @@
      * the process of going to sleep (checkReadyForSleep will be called when that process finishes).
      */
     boolean goToSleepIfPossible(boolean shuttingDown) {
-        if (!isLeafTask()) {
-            final int[] sleepInProgress = {0};
-            forAllLeafTasks((t) -> {
-                if (!t.goToSleepIfPossible(shuttingDown)) {
-                    sleepInProgress[0]++;
-                }
-            }, true);
-            return sleepInProgress[0] == 0;
-        }
-
-        boolean shouldSleep = true;
-        if (mResumedActivity != null) {
-            // Still have something resumed; can't sleep until it is paused.
-            ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
-            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                    "Sleep => pause with userLeaving=false");
-
-            startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
-                    "sleep");
-            shouldSleep = false ;
-        } else if (mPausingActivity != null) {
-            // Still waiting for something to pause; can't sleep yet.
-            ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
-            shouldSleep = false;
-        }
-
-        if (!shuttingDown) {
-            if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) {
-                // Still need to tell some activities to stop; can't sleep yet.
-                ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
-                        mTaskSupervisor.mStoppingActivities.size());
-
-                mTaskSupervisor.scheduleIdle();
-                shouldSleep = false;
+        final int[] sleepInProgress = {0};
+        forAllLeafTasksAndLeafTaskFragments(taskFragment -> {
+            if (!taskFragment.sleepIfPossible(shuttingDown)) {
+                sleepInProgress[0]++;
             }
-        }
-
-        if (shouldSleep) {
-            ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                    !PRESERVE_WINDOWS);
-        }
-
-        return shouldSleep;
-    }
-
-    private boolean containsActivityFromRootTask(List<ActivityRecord> rs) {
-        for (ActivityRecord r : rs) {
-            if (r.getRootTask() == this) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) {
-        return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
-    }
-
-    /**
-     * Start pausing the currently resumed activity.  It is an error to call this if there
-     * is already an activity being paused or there is no resumed activity.
-     *
-     * @param userLeaving True if this should result in an onUserLeaving to the current activity.
-     * @param uiSleeping True if this is happening with the user interface going to sleep (the
-     * screen turning off).
-     * @param resuming The activity we are currently trying to resume or null if this is not being
-     *                 called as part of resuming the top activity, so we shouldn't try to instigate
-     *                 a resume here if not null.
-     * @param reason The reason of pausing the activity.
-     * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
-     * it to tell us when it is done.
-     */
-    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
-            ActivityRecord resuming, String reason) {
-        if (!isLeafTask()) {
-            final int[] pausing = {0};
-            forAllLeafTasks((t) -> {
-                if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) {
-                    pausing[0]++;
-                }
-            }, true /* traverseTopToBottom */);
-            return pausing[0] > 0;
-        }
-
-        if (mPausingActivity != null) {
-            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
-                    + " state=" + mPausingActivity.getState());
-            if (!shouldSleepActivities()) {
-                // Avoid recursion among check for sleep and complete pause during sleeping.
-                // Because activity will be paused immediately after resume, just let pause
-                // be completed by the order of activity paused from clients.
-                completePauseLocked(false, resuming);
-            }
-        }
-        ActivityRecord prev = mResumedActivity;
-
-        if (prev == null) {
-            if (resuming == null) {
-                Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mRootWindowContainer.resumeFocusedTasksTopActivities();
-            }
-            return false;
-        }
-
-        if (prev == resuming) {
-            Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
-            return false;
-        }
-
-        ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
-        mPausingActivity = prev;
-        mLastPausedActivity = prev;
-        if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
-            mTaskSupervisor.mNoHistoryActivities.add(prev);
-        }
-        prev.setState(PAUSING, "startPausingLocked");
-        prev.getTask().touchActiveTime();
-
-        mAtmService.updateCpuStats();
-
-        boolean pauseImmediately = false;
-        boolean shouldAutoPip = false;
-        if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
-            // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
-            // activity to be paused, while at the same time resuming the new resume activity
-            // only if the previous activity can't go into Pip since we want to give Pip
-            // activities a chance to enter Pip before resuming the next activity.
-            final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
-                    "shouldResumeWhilePausing", userLeaving);
-            if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
-                shouldAutoPip = true;
-            } else if (!lastResumedCanPip) {
-                pauseImmediately = true;
-            } else {
-                // The previous activity may still enter PIP even though it did not allow auto-PIP.
-            }
-        }
-
-        boolean didAutoPip = false;
-        if (prev.attachedToProcess()) {
-            if (shouldAutoPip) {
-                ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
-                        + "directly: %s", prev);
-
-                didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
-                mPausingActivity = null;
-            } else {
-                ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
-                try {
-                    EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
-                            prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
-                    mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
-                            prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
-                                    prev.configChangeFlags, pauseImmediately));
-                } catch (Exception e) {
-                    // Ignore exception, if process died other code will cleanup.
-                    Slog.w(TAG, "Exception thrown during pause", e);
-                    mPausingActivity = null;
-                    mLastPausedActivity = null;
-                    mTaskSupervisor.mNoHistoryActivities.remove(prev);
-                }
-            }
-        } else {
-            mPausingActivity = null;
-            mLastPausedActivity = null;
-            mTaskSupervisor.mNoHistoryActivities.remove(prev);
-        }
-
-        // If we are not going to sleep, we want to ensure the device is
-        // awake until the next activity is started.
-        if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
-            mTaskSupervisor.acquireLaunchWakelock();
-        }
-
-        // If already entered PIP mode, no need to keep pausing.
-        if (mPausingActivity != null && !didAutoPip) {
-            // Have the window manager pause its key dispatching until the new
-            // activity has started.  If we're pausing the activity just because
-            // the screen is being turned off and the UI is sleeping, don't interrupt
-            // key dispatch; the same activity will pick it up again on wakeup.
-            if (!uiSleeping) {
-                prev.pauseKeyDispatchingLocked();
-            } else {
-                ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
-            }
-
-            if (pauseImmediately) {
-                // If the caller said they don't want to wait for the pause, then complete
-                // the pause now.
-                completePauseLocked(false, resuming);
-                return false;
-
-            } else {
-                prev.schedulePauseTimeout();
-                return true;
-            }
-
-        } else {
-            // This activity either failed to schedule the pause or it entered PIP mode,
-            // so just treat it as being paused now.
-            ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
-            if (resuming == null) {
-                mRootWindowContainer.resumeFocusedTasksTopActivities();
-            }
-            return false;
-        }
-    }
-
-    @VisibleForTesting
-    void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
-        // Complete the pausing process of a pausing activity, so it doesn't make sense to
-        // operate on non-leaf tasks.
-        warnForNonLeafTask("completePauseLocked");
-
-        ActivityRecord prev = mPausingActivity;
-        ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
-
-        if (prev != null) {
-            prev.setWillCloseOrEnterPip(false);
-            final boolean wasStopping = prev.isState(STOPPING);
-            prev.setState(PAUSED, "completePausedLocked");
-            if (prev.finishing) {
-                // We will update the activity visibility later, no need to do in
-                // completeFinishing(). Updating visibility here might also making the next
-                // activities to be resumed, and could result in wrong app transition due to
-                // lack of previous activity information.
-                ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
-                prev = prev.completeFinishing(false /* updateVisibility */,
-                        "completePausedLocked");
-            } else if (prev.hasProcess()) {
-                ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
-                        + "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,
-                        prev.mVisibleRequested);
-                if (prev.deferRelaunchUntilPaused) {
-                    // Complete the deferred relaunch that was waiting for pause to complete.
-                    ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
-                    prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
-                } else if (wasStopping) {
-                    // We are also stopping, the stop request must have gone soon after the pause.
-                    // We can't clobber it, because the stop confirmation will not be handled.
-                    // We don't need to schedule another stop, we only need to let it happen.
-                    prev.setState(STOPPING, "completePausedLocked");
-                } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
-                    // Clear out any deferred client hide we might currently have.
-                    prev.setDeferHidingClient(false);
-                    // If we were visible then resumeTopActivities will release resources before
-                    // stopping.
-                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
-                            "completePauseLocked");
-                }
-            } else {
-                ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
-                prev = null;
-            }
-            // It is possible the activity was freezing the screen before it was paused.
-            // In that case go ahead and remove the freeze this activity has on the screen
-            // since it is no longer visible.
-            if (prev != null) {
-                prev.stopFreezingScreenLocked(true /*force*/);
-            }
-            mPausingActivity = null;
-        }
-
-        if (resumeNext) {
-            final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
-            if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
-                mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null);
-            } else {
-                checkReadyForSleep();
-                final ActivityRecord top =
-                        topRootTask != null ? topRootTask.topRunningActivity() : null;
-                if (top == null || (prev != null && top != prev)) {
-                    // If there are no more activities available to run, do resume anyway to start
-                    // something. Also if the top activity on the root task is not the just paused
-                    // activity, we need to go ahead and resume it to ensure we complete an
-                    // in-flight app switch.
-                    mRootWindowContainer.resumeFocusedTasksTopActivities();
-                }
-            }
-        }
-
-        if (prev != null) {
-            prev.resumeKeyDispatchingLocked();
-        }
-
-        mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
-
-        // Notify when the task stack has changed, but only if visibilities changed (not just
-        // focus). Also if there is an active root pinned task - we always want to notify it about
-        // task stack changes, because its positioning may depend on it.
-        if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
-                || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
-            mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
-            mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
-        }
+        }, true /* traverseTopToBottom */);
+        return sleepInProgress[0] == 0;
     }
 
     boolean isTopRootTaskInDisplayArea() {
@@ -5942,10 +4822,10 @@
      *                 The activity is either starting or resuming.
      *                 Caller should ensure starting activity is visible.
      * @param preserveWindows Flag indicating whether windows should be preserved when updating
-     *                        configuration in {@link mEnsureActivitiesVisibleHelper}.
+     *                        configuration in {@link EnsureActivitiesVisibleHelper}.
      * @param configChanges Parts of the configuration that changed for this activity for evaluating
      *                      if the screen should be frozen as part of
-     *                      {@link mEnsureActivitiesVisibleHelper}.
+     *                      {@link EnsureActivitiesVisibleHelper}.
      *
      */
     void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
@@ -5961,21 +4841,22 @@
      *                 The activity is either starting or resuming.
      *                 Caller should ensure starting activity is visible.
      * @param notifyClients Flag indicating whether the visibility updates should be sent to the
-     *                      clients in {@link mEnsureActivitiesVisibleHelper}.
+     *                      clients in {@link EnsureActivitiesVisibleHelper}.
      * @param preserveWindows Flag indicating whether windows should be preserved when updating
-     *                        configuration in {@link mEnsureActivitiesVisibleHelper}.
+     *                        configuration in {@link EnsureActivitiesVisibleHelper}.
      * @param configChanges Parts of the configuration that changed for this activity for evaluating
      *                      if the screen should be frozen as part of
-     *                      {@link mEnsureActivitiesVisibleHelper}.
+     *                      {@link EnsureActivitiesVisibleHelper}.
      */
     // TODO: Should be re-worked based on the fact that each task as a root task in most cases.
     void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
         mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
-            forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
-                    starting, configChanges, preserveWindows, notifyClients),
-                    true /* traverseTopToBottom */);
+            forAllLeafTasks(task -> {
+                task.updateActivityVisibilities(starting, configChanges, preserveWindows,
+                        notifyClients);
+            }, true /* traverseTopToBottom */);
 
             // Notify WM shell that task visibilities may have changed
             forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
@@ -6050,25 +4931,6 @@
         }
     }
 
-    /** @see ActivityRecord#cancelInitializing() */
-    void cancelInitializingActivities() {
-        // We don't want to clear starting window for activities that aren't behind fullscreen
-        // activities as we need to display their starting window until they are done initializing.
-        checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
-    }
-
-    /**
-     * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
-     * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
-     * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
-     * activities to the function.
-     */
-    boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
-            Consumer<ActivityRecord> handleBehindFullscreenActivity) {
-        return mCheckBehindFullscreenActivityHelper.process(
-                toCheck, handleBehindFullscreenActivity);
-    }
-
     /**
      * Ensure that the top activity in the root task is resumed.
      *
@@ -6109,7 +4971,8 @@
                     if (!child.isTopActivityFocusable()) {
                         continue;
                     }
-                    if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
+                    if (child.getVisibility(null /* starting */)
+                            != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
                         break;
                     }
 
@@ -6156,383 +5019,23 @@
             return false;
         }
 
-        // Find the next top-most activity to resume in this root task that is not finishing and is
-        // focusable. If it is not focusable, we will fall into the case below to resume the
-        // top activity in the next focusable task.
-        ActivityRecord next = topRunningActivity(true /* focusableOnly */);
-
-        final boolean hasRunningActivity = next != null;
-
-        // TODO: Maybe this entire condition can get removed?
-        if (hasRunningActivity && !isAttached()) {
-            return false;
-        }
-
-        mRootWindowContainer.cancelInitializingActivities();
-
-        if (!hasRunningActivity) {
-            // There are no activities left in the root task, let's look somewhere else.
+        final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
+        if (topActivity == null) {
+            // There are no activities left in this task, let's look somewhere else.
             return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
         }
 
-        next.delayedResume = false;
-        final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
-        // If the top activity is the resumed one, nothing to do.
-        if (mResumedActivity == next && next.isState(RESUMED)
-                && taskDisplayArea.allResumedActivitiesComplete()) {
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            executeAppTransition(options);
-            // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
-            // we still want to check if the visibility of other windows have changed (e.g. bringing
-            // a fullscreen window forward to cover another freeform activity.)
-            if (taskDisplayArea.inMultiWindowMode()) {
-                taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */, true /* notifyClients */);
-            }
-            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity "
-                    + "resumed %s", next);
-            return false;
-        }
-
-        if (!next.canResumeByCompat()) {
-            return false;
-        }
-
-        // If we are currently pausing an activity, then don't do anything until that is done.
-        final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
-        if (!allPausedComplete) {
-            ProtoLog.v(WM_DEBUG_STATES,
-                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
-            return false;
-        }
-
-        // If we are sleeping, and there is no resumed activity, and the top activity is paused,
-        // well that is the state we want.
-        if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            executeAppTransition(options);
-            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and"
-                    + " all paused");
-            return false;
-        }
-
-        // Make sure that the user who owns this activity is started.  If not,
-        // we will just leave it as is because someone should be bringing
-        // another user's activities to the top of the stack.
-        if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
-            Slog.w(TAG, "Skipping resume of top activity " + next
-                    + ": user " + next.mUserId + " is stopped");
-            return false;
-        }
-
-        // The activity may be waiting for stop, but that is no longer
-        // appropriate for it.
-        mTaskSupervisor.mStoppingActivities.remove(next);
-
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
-        mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
-        ActivityRecord lastResumed = null;
-        final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
-        if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) {
-            // So, why aren't we using prev here??? See the param comment on the method. prev
-            // doesn't represent the last resumed activity. However, the last focus stack does if
-            // it isn't null.
-            lastResumed = lastFocusedRootTask.getResumedActivity();
-        }
-
-        boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
-        if (mResumedActivity != null) {
-            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
-            pausing |= startPausingLocked(false /* uiSleeping */, next,
-                    "resumeTopActivityInnerLocked");
-        }
-        if (pausing) {
-            ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to"
-                    + " start pausing");
-            // At this point we want to put the upcoming activity's process
-            // at the top of the LRU list, since we know we will be needing it
-            // very soon and it would be a waste to let it get killed if it
-            // happens to be sitting towards the end.
-            if (next.attachedToProcess()) {
-                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
-                        true /* activityChange */, false /* updateOomAdj */,
-                        false /* addPendingTopUid */);
-            } else if (!next.isProcessRunning()) {
-                // Since the start-process is asynchronous, if we already know the process of next
-                // activity isn't running, we can start the process earlier to save the time to wait
-                // for the current activity to be paused.
-                final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
-                mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
-                        isTop ? "pre-top-activity" : "pre-activity");
-            }
-            if (lastResumed != null) {
-                lastResumed.setWillCloseOrEnterPip(true);
-            }
-            return true;
-        } else if (mResumedActivity == next && next.isState(RESUMED)
-                && taskDisplayArea.allResumedActivitiesComplete()) {
-            // It is possible for the activity to be resumed when we paused back stacks above if the
-            // next activity doesn't have to wait for pause to complete.
-            // So, nothing else to-do except:
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            executeAppTransition(options);
-            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed "
-                    + "(dontWaitForPause) %s", next);
-            return true;
-        }
-
-        // If the most recent activity was noHistory but was only stopped rather
-        // than stopped+finished because the device went to sleep, we need to make
-        // sure to finish it as we're making a new activity topmost.
-        if (shouldSleepActivities()) {
-            mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
-        }
-
-        if (prev != null && prev != next && next.nowVisible) {
-
-            // The next activity is already visible, so hide the previous
-            // activity's windows right now so we can show the new one ASAP.
-            // We only do this if the previous is finishing, which should mean
-            // it is on top of the one being resumed so hiding it quickly
-            // is good.  Otherwise, we want to do the normal route of allowing
-            // the resumed activity to be shown so we can decide if the
-            // previous should actually be hidden depending on whether the
-            // new one is found to be full-screen or not.
-            if (prev.finishing) {
-                prev.setVisibility(false);
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                        "Not waiting for visible to hide: " + prev
-                                + ", nowVisible=" + next.nowVisible);
-            } else {
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                        "Previous already visible but still waiting to hide: " + prev
-                                + ", nowVisible=" + next.nowVisible);
+        final boolean[] resumed = new boolean[1];
+        final TaskFragment topFragment = topActivity.getTaskFragment();
+        resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
+        forAllLeafTaskFragments(f -> {
+            if (topFragment == f) {
+                return;
             }
 
-        }
-
-        // Launching this app's activity, make sure the app is no longer
-        // considered stopped.
-        try {
-            mTaskSupervisor.getActivityMetricsLogger()
-                    .notifyBeforePackageUnstopped(next.packageName);
-            mAtmService.getPackageManager().setPackageStoppedState(
-                    next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
-        } catch (RemoteException e1) {
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Failed trying to unstop package "
-                    + next.packageName + ": " + e);
-        }
-
-        // We are starting up the next activity, so tell the window manager
-        // that the previous one will be hidden soon.  This way it can know
-        // to ignore it when computing the desired screen orientation.
-        boolean anim = true;
-        final DisplayContent dc = taskDisplayArea.mDisplayContent;
-        if (prev != null) {
-            if (prev.finishing) {
-                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                        "Prepare close transition: prev=" + prev);
-                if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
-                    anim = false;
-                    dc.prepareAppTransition(TRANSIT_NONE);
-                } else {
-                    dc.prepareAppTransition(TRANSIT_CLOSE);
-                }
-                prev.setVisibility(false);
-            } else {
-                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                        "Prepare open transition: prev=" + prev);
-                if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
-                    anim = false;
-                    dc.prepareAppTransition(TRANSIT_NONE);
-                } else {
-                    dc.prepareAppTransition(TRANSIT_OPEN,
-                            next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
-                }
-            }
-        } else {
-            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
-            if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
-                anim = false;
-                dc.prepareAppTransition(TRANSIT_NONE);
-            } else {
-                dc.prepareAppTransition(TRANSIT_OPEN);
-            }
-        }
-
-        if (anim) {
-            next.applyOptionsAnimation();
-        } else {
-            next.abortAndClearOptionsAnimation();
-        }
-
-        mTaskSupervisor.mNoAnimActivities.clear();
-
-        if (next.attachedToProcess()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
-                    + " stopped=" + next.stopped
-                    + " visibleRequested=" + next.mVisibleRequested);
-
-            // If the previous activity is translucent, force a visibility update of
-            // the next activity, so that it's added to WM's opening app list, and
-            // transition animation can be set up properly.
-            // For example, pressing Home button with a translucent activity in focus.
-            // Launcher is already visible in this case. If we don't add it to opening
-            // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
-            // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
-            final boolean lastActivityTranslucent = lastFocusedRootTask != null
-                    && (lastFocusedRootTask.inMultiWindowMode()
-                    || (lastFocusedRootTask.mLastPausedActivity != null
-                    && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
-
-            // This activity is now becoming visible.
-            if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
-                next.setVisibility(true);
-            }
-
-            // schedule launch ticks to collect information about slow apps.
-            next.startLaunchTickingLocked();
-
-            ActivityRecord lastResumedActivity =
-                    lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity();
-            final ActivityState lastState = next.getState();
-
-            mAtmService.updateCpuStats();
-
-            ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
-
-            next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
-            // Have the window manager re-evaluate the orientation of
-            // the screen based on the new activity order.
-            boolean notUpdated = true;
-
-            // Activity should also be visible if set mLaunchTaskBehind to true (see
-            // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
-            if (shouldBeVisible(next)) {
-                // We have special rotation behavior when here is some active activity that
-                // requests specific orientation or Keyguard is locked. Make sure all activity
-                // visibilities are set correctly as well as the transition is updated if needed
-                // to get the correct rotation behavior. Otherwise the following call to update
-                // the orientation may cause incorrect configurations delivered to client as a
-                // result of invisible window resize.
-                // TODO: Remove this once visibilities are set correctly immediately when
-                // starting an activity.
-                notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
-                        true /* markFrozenIfConfigChanged */, false /* deferResume */);
-            }
-
-            if (notUpdated) {
-                // The configuration update wasn't able to keep the existing
-                // instance of the activity, and instead started a new one.
-                // We should be all done, but let's just make sure our activity
-                // is still at the top and schedule another run if something
-                // weird happened.
-                ActivityRecord nextNext = topRunningActivity();
-                ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
-                        + "%s, new next: %s", next, nextNext);
-                if (nextNext != next) {
-                    // Do over!
-                    mTaskSupervisor.scheduleResumeTopActivities();
-                }
-                if (!next.mVisibleRequested || next.stopped) {
-                    next.setVisibility(true);
-                }
-                next.completeResumeLocked();
-                return true;
-            }
-
-            try {
-                final ClientTransaction transaction =
-                        ClientTransaction.obtain(next.app.getThread(), next.appToken);
-                // Deliver all pending results.
-                ArrayList<ResultInfo> a = next.results;
-                if (a != null) {
-                    final int N = a.size();
-                    if (!next.finishing && N > 0) {
-                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
-                                "Delivering results to " + next + ": " + a);
-                        transaction.addCallback(ActivityResultItem.obtain(a));
-                    }
-                }
-
-                if (next.newIntents != null) {
-                    transaction.addCallback(
-                            NewIntentItem.obtain(next.newIntents, true /* resume */));
-                }
-
-                // Well the app will no longer be stopped.
-                // Clear app token stopped state in window manager if needed.
-                next.notifyAppResumed(next.stopped);
-
-                EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
-                        next.getTask().mTaskId, next.shortComponentName);
-
-                mAtmService.getAppWarningsLocked().onResumeActivity(next);
-                next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
-                next.abortAndClearOptionsAnimation();
-                transaction.setLifecycleStateRequest(
-                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
-                                dc.isNextTransitionForward()));
-                mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-
-                ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
-            } catch (Exception e) {
-                // Whoops, need to restart this activity!
-                ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
-                        + "%s", lastState, next);
-                next.setState(lastState, "resumeTopActivityInnerLocked");
-
-                // lastResumedActivity being non-null implies there is a lastStack present.
-                if (lastResumedActivity != null) {
-                    lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
-                }
-
-                Slog.i(TAG, "Restarting because process died: " + next);
-                if (!next.hasBeenLaunched) {
-                    next.hasBeenLaunched = true;
-                } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
-                        && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
-                    next.showStartingWindow(false /* taskSwitch */);
-                }
-                mTaskSupervisor.startSpecificActivity(next, true, false);
-                return true;
-            }
-
-            // From this point on, if something goes wrong there is no way
-            // to recover the activity.
-            try {
-                next.completeResumeLocked();
-            } catch (Exception e) {
-                // If any exception gets thrown, toss away this
-                // activity and try the next one.
-                Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                next.finishIfPossible("resume-exception", true /* oomAdj */);
-                return true;
-            }
-        } else {
-            // Whoops, need to restart this activity!
-            if (!next.hasBeenLaunched) {
-                next.hasBeenLaunched = true;
-            } else {
-                if (SHOW_APP_STARTING_PREVIEW) {
-                    next.showStartingWindow(false /* taskSwich */);
-                }
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
-            }
-            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next);
-            mTaskSupervisor.startSpecificActivity(next, true, true);
-        }
-
-        return true;
+            resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
+        }, true);
+        return resumed[0];
     }
 
     /**
@@ -6612,7 +5115,6 @@
         // Slot the activity into the history root task and proceed
         ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
                         + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
-        task.positionChildAtTop(r);
 
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
         // is false, because the activity won't be visible.
@@ -6674,7 +5176,8 @@
                 // window manager to keep the previous window it had previously
                 // created, if it still had one.
                 Task prevTask = r.getTask();
-                ActivityRecord prev = prevTask.topActivityWithStartingWindow();
+                ActivityRecord prev = prevTask.getActivity(
+                        a -> a.mStartingData != null && a.okToShowLocked());
                 if (prev != null) {
                     // We don't want to reuse the previous starting preview if:
                     // (1) The current activity is in a different task.
@@ -7188,13 +5691,6 @@
         return true;
     }
 
-    /**
-     * Ensures all visible activities at or below the input activity have the right configuration.
-     */
-    void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
-        mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
-    }
-
     // TODO: Can only be called from special methods in ActivityTaskSupervisor.
     // Need to consolidate those calls points into this resize method so anyone can call directly.
     void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
@@ -7248,114 +5744,35 @@
         }
     }
 
-    /**
-     * Reset local parameters because an app's activity died.
-     * @param app The app of the activity that died.
-     * @return {@code true} if the process of the pausing activity is died.
-     */
-    boolean handleAppDied(WindowProcessController app) {
-        warnForNonLeafTask("handleAppDied");
-        boolean isPausingDied = false;
-        if (mPausingActivity != null && mPausingActivity.app == app) {
-            ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
-                    mPausingActivity);
-            mPausingActivity = null;
-            isPausingDied = true;
-        }
-        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
-            if (mLastPausedActivity.isNoHistory()) {
-                mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
-            }
-            mLastPausedActivity = null;
-        }
-        return isPausingDied;
-    }
-
     boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
             String dumpPackage, final boolean needSep) {
-        Runnable headerPrinter = () -> {
-            if (needSep) {
-                pw.println();
-            }
-            pw.println("  RootTask #" + getRootTaskId()
-                    + ": type=" + activityTypeToString(getActivityType())
-                    + " mode=" + windowingModeToString(getWindowingMode()));
-            pw.println("  isSleeping=" + shouldSleepActivities());
-            pw.println("  mBounds=" + getRequestedOverrideBounds());
-            pw.println("  mCreatedByOrganizer=" + mCreatedByOrganizer);
-        };
-
-        boolean printed = false;
-
-        if (dumpPackage == null) {
-            // If we are not filtering by package, we want to print absolutely everything,
-            // so always print the header even if there are no tasks/activities inside.
-            headerPrinter.run();
-            headerPrinter = null;
-            printed = true;
-        }
-
-        printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false,
-                "    mPausingActivity: ", null);
-        printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
-                "    mResumedActivity: ", null);
-        if (dumpAll) {
-            printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
-                    "    mLastPausedActivity: ", null);
-        }
-
-        printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
-        return printed;
+        return dump("  ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */);
     }
 
-    private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
-        if (!hasChild()) {
-            return false;
+    @Override
+    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+        pw.print(prefix); pw.print("* "); pw.println(this);
+        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
+        pw.println(prefix + "  mCreatedByOrganizer=" + mCreatedByOrganizer);
+        if (mLastNonFullscreenBounds != null) {
+            pw.print(prefix); pw.print("  mLastNonFullscreenBounds=");
+            pw.println(mLastNonFullscreenBounds);
         }
-        final AtomicBoolean printedHeader = new AtomicBoolean(false);
-        final AtomicBoolean printed = new AtomicBoolean(false);
-        forAllLeafTasks((task) -> {
-            final String prefix = "    ";
-            Runnable headerPrinter = () -> {
-                printed.set(true);
-                if (!printedHeader.get()) {
-                    if (needSep) {
-                        pw.println("");
-                    }
-                    if (header != null) {
-                        header.run();
-                    }
-                    printedHeader.set(true);
-                }
-                pw.print(prefix); pw.print("* "); pw.println(task);
-                pw.print(prefix); pw.print("  mBounds=");
-                pw.println(task.getRequestedOverrideBounds());
-                pw.print(prefix); pw.print("  mMinWidth="); pw.print(task.mMinWidth);
-                pw.print(" mMinHeight="); pw.println(task.mMinHeight);
-                if (mLastNonFullscreenBounds != null) {
-                    pw.print(prefix);
-                    pw.print("  mLastNonFullscreenBounds=");
-                    pw.println(task.mLastNonFullscreenBounds);
-                }
-                task.dump(pw, prefix + "  ");
-            };
-            if (dumpPackage == null) {
-                // If we are not filtering by package, we want to print absolutely everything,
-                // so always print the header even if there are no activities inside.
-                headerPrinter.run();
-                headerPrinter = null;
+        if (dumpAll) {
+            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+                    prefix + "  mLastPausedActivity: ", null);
+        }
+        if (isLeafTask()) {
+            pw.println(prefix + "  isSleeping=" + shouldSleepActivities());
+            printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
+                    prefix + "  topPausingActivity=", null);
+            printThisActivity(pw, getTopResumedActivity(), dumpPackage, false,
+                    prefix + "  topResumedActivity=", null);
+            if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
+                pw.print(prefix); pw.print("  mMinWidth="); pw.print(mMinWidth);
+                pw.print(" mMinHeight="); pw.println(mMinHeight);
             }
-            final ArrayList<ActivityRecord> activities = new ArrayList<>();
-            // Add activities by traversing the hierarchy from bottom to top, since activities
-            // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}.
-            task.forAllActivities((Consumer<ActivityRecord>) activities::add,
-                    false /* traverseTopToBottom */);
-            dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
-                    dumpPackage, false, headerPrinter, task);
-        }, true /* traverseTopToBottom */);
-        return printed.get();
+        }
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -7740,7 +6157,8 @@
 
     /** Returns true if a removal action is still being deferred. */
     boolean handleCompleteDeferredRemoval() {
-        if (isAnimating(TRANSITION | CHILDREN)) {
+        if (isAnimating(TRANSITION | CHILDREN)
+                || mAtmService.getTransitionController().inTransition(this)) {
             return true;
         }
 
@@ -7755,6 +6173,7 @@
         return mAnimatingActivityRegistry;
     }
 
+    @Override
     void executeAppTransition(ActivityOptions options) {
         mDisplayContent.executeAppTransition();
         ActivityOptions.abort(options);
@@ -7777,10 +6196,6 @@
         return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
     }
 
-    boolean shouldSleepOrShutDownActivities() {
-        return shouldSleepActivities() || mAtmService.mShuttingDown;
-    }
-
     private Rect getRawBounds() {
         return super.getBounds();
     }
@@ -7799,14 +6214,12 @@
         }
 
         final long token = proto.start(fieldId);
-        super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
 
         proto.write(TaskProto.ID, mTaskId);
-        proto.write(DISPLAY_ID, getDisplayId());
         proto.write(ROOT_TASK_ID, getRootTaskId());
 
-        if (mResumedActivity != null) {
-            mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+        if (getTopResumedActivity() != null) {
+            getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY);
         }
         if (realActivity != null) {
             proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
@@ -7814,11 +6227,7 @@
         if (origActivity != null) {
             proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
         }
-        proto.write(ACTIVITY_TYPE, getActivityType());
         proto.write(RESIZE_MODE, mResizeMode);
-        proto.write(MIN_WIDTH, mMinWidth);
-        proto.write(MIN_HEIGHT, mMinHeight);
-
         proto.write(FILLS_PARENT, matchParentBounds());
         getRawBounds().dumpDebug(proto, BOUNDS);
 
@@ -7835,6 +6244,8 @@
         proto.write(AFFINITY, affinity);
         proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
 
+        super.dumpDebug(proto, TASK_FRAGMENT, logLevel);
+
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index d450dbf..10c16ff 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -34,11 +34,10 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
 import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -469,7 +468,7 @@
         // Update the top resumed activity because the preferred top focusable task may be changed.
         mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
 
-        final ActivityRecord r = child.getResumedActivity();
+        final ActivityRecord r = child.getTopResumedActivity();
         if (r != null && r == mRootWindowContainer.getTopResumedActivity()) {
             mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt");
         }
@@ -1231,7 +1230,7 @@
                                 + adjacentFlagRootTask);
             }
 
-            if (adjacentFlagRootTask.mAdjacentTask == null) {
+            if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) {
                 throw new UnsupportedOperationException(
                         "Can't set non-adjacent root as launch adjacent flag root tr="
                                 + adjacentFlagRootTask);
@@ -1269,8 +1268,8 @@
             // If the adjacent launch is coming from the same root, launch to adjacent root instead.
             if (sourceTask != null
                     && sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId
-                    && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) {
-                return mLaunchAdjacentFlagRootTask.mAdjacentTask;
+                    && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null) {
+                return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
             } else {
                 return mLaunchAdjacentFlagRootTask;
             }
@@ -1280,8 +1279,10 @@
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
                 final Task launchRootTask = mLaunchRootTasks.get(i).task;
                 // Return the focusable root task for improving the UX with staged split screen.
-                final Task adjacentRootTask = launchRootTask != null
-                        ? launchRootTask.mAdjacentTask : null;
+                final TaskFragment adjacentTaskFragment = launchRootTask != null
+                        ? launchRootTask.getAdjacentTaskFragment() : null;
+                final Task adjacentRootTask =
+                        adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
                 if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
                     return adjacentRootTask;
                 } else {
@@ -1373,11 +1374,11 @@
         }
         // TODO(b/111541062): Move this into Task#getResumedActivity()
         // Check if the focused root task has the resumed activity
-        ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+        ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
         if (resumedActivity == null || resumedActivity.app == null) {
             // If there is no registered resumed activity in the root task or it is not running -
             // try to use previously resumed one.
-            resumedActivity = focusedRootTask.getPausingActivity();
+            resumedActivity = focusedRootTask.getTopPausingActivity();
             if (resumedActivity == null || resumedActivity.app == null) {
                 // If previously resumed activity doesn't work either - find the topmost running
                 // activity that can be focused.
@@ -1404,7 +1405,7 @@
         // Clear last paused activity if focused root task changed while sleeping, so that the
         // top activity of current focused task can be resumed.
         if (mDisplayContent.isSleeping()) {
-            currentFocusedTask.mLastPausedActivity = null;
+            currentFocusedTask.clearLastPausedActivity();
         }
 
         mLastFocusedRootTask = prevFocusedTask;
@@ -1425,7 +1426,7 @@
                 continue;
             }
 
-            final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity();
+            final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity();
             if (r != null && !r.isState(RESUMED)) {
                 return false;
             }
@@ -1451,18 +1452,30 @@
      */
     boolean pauseBackTasks(ActivityRecord resuming) {
         final int[] someActivityPaused = {0};
-        forAllLeafTasks((task) -> {
-            final ActivityRecord resumedActivity = task.getResumedActivity();
-            if (resumedActivity != null
-                    && (task.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
-                    || !task.isTopActivityFocusable())) {
-                ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s "
-                        + "mResumedActivity=%s", task, resumedActivity);
-                if (task.startPausingLocked(false /* uiSleeping*/,
-                        resuming, "pauseBackTasks")) {
-                    someActivityPaused[0]++;
+        forAllLeafTasks(leafTask -> {
+            // Check if the direct child resumed activity in the leaf task needed to be paused if
+            // the leaf task is not a leaf task fragment.
+            if (!leafTask.isLeafTaskFragment()) {
+                final ActivityRecord top = topRunningActivity();
+                final ActivityRecord resumedActivity = leafTask.getResumedActivity();
+                if (resumedActivity != null && top.getTaskFragment() != leafTask) {
+                    // Pausing the resumed activity because it is occluded by other task fragment.
+                    if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+                        someActivityPaused[0]++;
+                    }
                 }
             }
+
+            leafTask.forAllLeafTaskFragments((taskFrag) -> {
+                final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+                if (resumedActivity != null
+                        && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
+                        || !taskFrag.isTopActivityFocusable())) {
+                    if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+                        someActivityPaused[0]++;
+                    }
+                }
+            }, true /* traverseTopToBottom */);
         }, true /* traverseTopToBottom */);
         return someActivityPaused[0] > 0;
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
new file mode 100644
index 0000000..9159bbb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -0,0 +1,2024 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+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 android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.os.UserHandle.USER_NULL;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
+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.TaskFragmentProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskFragmentProto.DISPLAY_ID;
+import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
+import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ResultInfo;
+import android.app.WindowConfiguration;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A basic container that can be used to contain activities or other {@link TaskFragment}, which
+ * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
+ */
+class TaskFragment extends WindowContainer<WindowContainer> {
+    @IntDef(prefix = {"TASK_FRAGMENT_VISIBILITY"}, value = {
+            TASK_FRAGMENT_VISIBILITY_VISIBLE,
+            TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+            TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+    })
+    @interface TaskFragmentVisibility {}
+
+    /**
+     * TaskFragment is visible. No other TaskFragment(s) on top that fully or partially occlude it.
+     */
+    static final int TASK_FRAGMENT_VISIBILITY_VISIBLE = 0;
+
+    /** TaskFragment is partially occluded by other translucent TaskFragment(s) on top of it. */
+    static final int TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+
+    /** TaskFragment is completely invisible. */
+    static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2;
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+
+    /** Set to false to disable the preview that is shown while a new activity is being started. */
+    static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+    /**
+     * Indicate that the minimal width/height should use the default value.
+     *
+     * @see #mMinWidth
+     * @see #mMinHeight
+     */
+    static final int INVALID_MIN_SIZE = -1;
+
+    final ActivityTaskManagerService mAtmService;
+    final ActivityTaskSupervisor mTaskSupervisor;
+    final RootWindowContainer mRootWindowContainer;
+    private final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
+
+    /**
+     * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+     * should use the default minimal width.
+     */
+    int mMinWidth;
+
+    /**
+     * Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+     * should use the default minimal height.
+     */
+    int mMinHeight;
+
+    /** Avoid reentrant of {@link #removeImmediately()}. */
+    private boolean mRemoving;
+
+    // The TaskFragment that adjacent to this one.
+    private TaskFragment mAdjacentTaskFragment;
+
+    /**
+     * Prevents duplicate calls to onTaskAppeared.
+     */
+    boolean mTaskFragmentAppearedSent;
+
+    /**
+     * When we are in the process of pausing an activity, before starting the
+     * next one, this variable holds the activity that is currently being paused.
+     *
+     * Only set at leaf task fragments.
+     */
+    @Nullable
+    private ActivityRecord mPausingActivity = null;
+
+    /**
+     * This is the last activity that we put into the paused state.  This is
+     * used to determine if we need to do an activity transition while sleeping,
+     * when we normally hold the top activity paused.
+     */
+    ActivityRecord mLastPausedActivity = null;
+
+    /**
+     * Current activity that is resumed, or null if there is none.
+     * Only set at leaf task fragments.
+     */
+    @Nullable
+    private ActivityRecord mResumedActivity = null;
+
+    /**
+     * This TaskFragment was created by an organizer which has the following implementations.
+     * <ul>
+     *     <li>The TaskFragment won't be removed when it is empty. Removal has to be an explicit
+     *     request from the organizer.</li>
+     *     <li>If this fragment is a Task object then unlike other non-root tasks, it's direct
+     *     children are visible to the organizer for ordering purposes.</li>
+     *     <li>A TaskFragment can be created by {@link android.window.TaskFragmentOrganizer}, and
+     *     a Task can be created by {@link android.window.TaskOrganizer}.</li>
+     * </ul>
+     */
+    @VisibleForTesting
+    boolean mCreatedByOrganizer;
+
+    /** Organizer that organizing this TaskFragment. */
+    @Nullable
+    private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
+    /** Client assigned unique token for this TaskFragment if this is created by an organizer. */
+    @Nullable
+    private IBinder mFragmentToken;
+
+    /**
+     * The PID of the organizer that created this TaskFragment. It should be the same as the PID
+     * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
+     * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
+     */
+    private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
+
+    private final Rect mTmpInsets = new Rect();
+    private final Rect mTmpBounds = new Rect();
+    private final Rect mTmpFullBounds = new Rect();
+    private final Rect mTmpStableBounds = new Rect();
+    private final Rect mTmpNonDecorBounds = new Rect();
+
+    private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+            new EnsureActivitiesVisibleHelper(this);
+    private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+            new EnsureVisibleActivitiesConfigHelper();
+    private class EnsureVisibleActivitiesConfigHelper {
+        private boolean mUpdateConfig;
+        private boolean mPreserveWindow;
+        private boolean mBehindFullscreen;
+
+        void reset(boolean preserveWindow) {
+            mPreserveWindow = preserveWindow;
+            mUpdateConfig = false;
+            mBehindFullscreen = false;
+        }
+
+        void process(ActivityRecord start, boolean preserveWindow) {
+            if (start == null || !start.mVisibleRequested) {
+                return;
+            }
+            reset(preserveWindow);
+
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    EnsureVisibleActivitiesConfigHelper::processActivity, this,
+                    PooledLambda.__(ActivityRecord.class));
+            forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+            f.recycle();
+
+            if (mUpdateConfig) {
+                // Ensure the resumed state of the focus activity if we updated the configuration of
+                // any activity.
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
+            }
+        }
+
+        boolean processActivity(ActivityRecord r) {
+            mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+            mBehindFullscreen |= r.occludesParent();
+            return mBehindFullscreen;
+        }
+    }
+
+    TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
+            boolean createdByOrganizer) {
+        super(atmService.mWindowManager);
+
+        mAtmService = atmService;
+        mTaskSupervisor = mAtmService.mTaskSupervisor;
+        mRootWindowContainer = mAtmService.mRootWindowContainer;
+        mCreatedByOrganizer = createdByOrganizer;
+        mTaskFragmentOrganizerController =
+                mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+        mFragmentToken = fragmentToken;
+        mRemoteToken = new RemoteToken(this);
+    }
+
+    void setAdjacentTaskFragment(TaskFragment taskFragment) {
+        mAdjacentTaskFragment = taskFragment;
+        taskFragment.mAdjacentTaskFragment = this;
+    }
+
+    void setTaskFragmentOrganizer(ITaskFragmentOrganizer organizer, int pid) {
+        mTaskFragmentOrganizer = organizer;
+        mTaskFragmentOrganizerPid = pid;
+    }
+
+    /** Whether this TaskFragment is organized by the given {@code organizer}. */
+    boolean hasTaskFragmentOrganizer(ITaskFragmentOrganizer organizer) {
+        return organizer != null && mTaskFragmentOrganizer != null
+                && organizer.asBinder().equals(mTaskFragmentOrganizer.asBinder());
+    }
+
+    TaskFragment getAdjacentTaskFragment() {
+        return mAdjacentTaskFragment;
+    }
+
+    /** @return the currently resumed activity. */
+    ActivityRecord getResumedActivity() {
+        return mResumedActivity;
+    }
+
+    void setResumedActivity(ActivityRecord r, String reason) {
+        warnForNonLeafTaskFragment("setResumedActivity");
+        if (mResumedActivity == r) {
+            return;
+        }
+
+        if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+            Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+                    + mResumedActivity + " to:" + r + " reason:" + reason);
+        }
+        mResumedActivity = r;
+        mTaskSupervisor.updateTopResumedActivityIfNeeded();
+    }
+
+    @VisibleForTesting
+    void setPausingActivity(ActivityRecord pausing) {
+        mPausingActivity = pausing;
+    }
+
+    ActivityRecord getPausingActivity() {
+        return mPausingActivity;
+    }
+
+    int getDisplayId() {
+        final DisplayContent dc = getDisplayContent();
+        return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
+    }
+
+    @Nullable
+    Task getTask() {
+        if (asTask() != null) {
+            return asTask();
+        }
+
+        TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null;
+        return parent != null ? parent.getTask() : null;
+    }
+
+    @Override
+    @Nullable
+    TaskDisplayArea getDisplayArea() {
+        return (TaskDisplayArea) super.getDisplayArea();
+    }
+
+    @Override
+    public boolean isAttached() {
+        final TaskDisplayArea taskDisplayArea = getDisplayArea();
+        return taskDisplayArea != null && !taskDisplayArea.isRemoved();
+    }
+
+    /**
+     * Returns the root {@link TaskFragment}, which is usually also a {@link Task}.
+     */
+    @NonNull
+    TaskFragment getRootTaskFragment() {
+        final WindowContainer parent = getParent();
+        if (parent == null) return this;
+
+        final TaskFragment parentTaskFragment = parent.asTaskFragment();
+        return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
+    }
+
+    @Override
+    TaskFragment asTaskFragment() {
+        return this;
+    }
+
+
+    /**
+     * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
+     */
+    private void warnForNonLeafTaskFragment(String func) {
+        if (!isLeafTaskFragment()) {
+            Slog.w(TAG, func + " on non-leaf task fragment " + this);
+        }
+    }
+
+    boolean hasDirectChildActivities() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).asActivityRecord() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void cleanUpActivityReferences(@NonNull ActivityRecord r) {
+        if (mPausingActivity != null && mPausingActivity == r) {
+            mPausingActivity = null;
+        }
+
+        if (mResumedActivity != null && mResumedActivity == r) {
+            setResumedActivity(null, "cleanUpActivityReferences");
+        }
+        r.removeTimeouts();
+    }
+
+    /**
+     * Returns whether this TaskFragment is currently forced to be hidden for any reason.
+     */
+    protected boolean isForceHidden() {
+        return false;
+    }
+
+    boolean isLeafTaskFragment() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).asTaskFragment() != null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This should be called when an child activity changes state. This should only
+     * be called from
+     * {@link ActivityRecord#setState(ActivityRecord.State, String)} .
+     * @param record The {@link ActivityRecord} whose state has changed.
+     * @param state The new state.
+     * @param reason The reason for the change.
+     */
+    void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
+            String reason) {
+        warnForNonLeafTaskFragment("onActivityStateChanged");
+        if (record == mResumedActivity && state != RESUMED) {
+            setResumedActivity(null, reason + " - onActivityStateChanged");
+        }
+
+        if (state == RESUMED) {
+            if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+                Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
+            }
+            setResumedActivity(record, reason + " - onActivityStateChanged");
+            if (record == mRootWindowContainer.getTopResumedActivity()) {
+                mAtmService.setResumedActivityUncheckLocked(record, reason);
+            }
+            mTaskSupervisor.mRecentTasks.add(record.getTask());
+        }
+    }
+
+    /**
+     * Resets local parameters because an app's activity died.
+     * @param app The app of the activity that died.
+     * @return {@code true} if the process of the pausing activity is died.
+     */
+    boolean handleAppDied(WindowProcessController app) {
+        warnForNonLeafTaskFragment("handleAppDied");
+        boolean isPausingDied = false;
+        if (mPausingActivity != null && mPausingActivity.app == app) {
+            ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
+                    mPausingActivity);
+            mPausingActivity = null;
+            isPausingDied = true;
+        }
+        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+            if (mLastPausedActivity.isNoHistory()) {
+                mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
+            }
+            mLastPausedActivity = null;
+        }
+        return isPausingDied;
+    }
+
+    void awakeFromSleeping() {
+        if (mPausingActivity != null) {
+            Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause");
+            mPausingActivity.activityPaused(true);
+        }
+    }
+
+    /**
+     * Tries to put the activities in the task fragment to sleep.
+     *
+     * If the task fragment is not in a state where its activities can be put to sleep, this
+     * function will start any necessary actions to move the task fragment into such a state.
+     * It is expected that this function get called again when those actions complete.
+     *
+     * @param shuttingDown {@code true} when the called because the device is shutting down.
+     * @return true if the root task finished going to sleep, false if the root task only started
+     * the process of going to sleep (checkReadyForSleep will be called when that process finishes).
+     */
+    boolean sleepIfPossible(boolean shuttingDown) {
+        boolean shouldSleep = true;
+        if (mResumedActivity != null) {
+            // Still have something resumed; can't sleep until it is paused.
+            ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
+            startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+                    "sleep");
+            shouldSleep = false;
+        } else if (mPausingActivity != null) {
+            // Still waiting for something to pause; can't sleep yet.
+            ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
+            shouldSleep = false;
+        }
+
+        if (!shuttingDown) {
+            if (containsStoppingActivity()) {
+                // Still need to tell some activities to stop; can't sleep yet.
+                ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
+                        mTaskSupervisor.mStoppingActivities.size());
+
+                mTaskSupervisor.scheduleIdle();
+                shouldSleep = false;
+            }
+        }
+
+        if (shouldSleep) {
+            updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
+                    !PRESERVE_WINDOWS, true /* notifyClients */);
+        }
+
+        return shouldSleep;
+    }
+
+    private boolean containsStoppingActivity() {
+        for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) {
+            ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i);
+            if (r.getTaskFragment() == this) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the TaskFragment is translucent and can have other contents visible behind
+     * it if needed. A TaskFragment is considered translucent if it don't contain a visible or
+     * starting (about to be visible) activity that is fullscreen (opaque).
+     * @param starting The currently starting activity or null if there is none.
+     */
+    @VisibleForTesting
+    boolean isTranslucent(ActivityRecord starting) {
+        if (!isAttached() || isForceHidden()) {
+            return true;
+        }
+        final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
+                PooledLambda.__(ActivityRecord.class), starting);
+        final ActivityRecord opaque = getActivity(p);
+        p.recycle();
+        return opaque == null;
+    }
+
+    private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+        if (r.finishing) {
+            // We don't factor in finishing activities when determining translucency since
+            // they will be gone soon.
+            return false;
+        }
+
+        if (!r.visibleIgnoringKeyguard && r != starting) {
+            // Also ignore invisible activities that are not the currently starting
+            // activity (about to be visible).
+            return false;
+        }
+
+        if (r.occludesParent()) {
+            // Root task isn't translucent if it has at least one fullscreen activity
+            // that is visible.
+            return true;
+        }
+        return false;
+    }
+
+    ActivityRecord topRunningActivity() {
+        return topRunningActivity(false /* focusableOnly */);
+    }
+
+    ActivityRecord topRunningActivity(boolean focusableOnly) {
+        // Split into 2 to avoid object creation due to variable capture.
+        if (focusableOnly) {
+            return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+        } else {
+            return getActivity(ActivityRecord::canBeTopRunning);
+        }
+    }
+
+    boolean isTopActivityFocusable() {
+        final ActivityRecord r = topRunningActivity();
+        return r != null ? r.isFocusable()
+                : (isFocusable() && getWindowConfiguration().canReceiveKeys());
+    }
+
+    /**
+     * Returns the visibility state of this TaskFragment.
+     *
+     * @param starting The currently starting activity or null if there is none.
+     */
+    @TaskFragmentVisibility
+    int getVisibility(ActivityRecord starting) {
+        if (!isAttached() || isForceHidden()) {
+            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+        }
+
+        if (isTopActivityLaunchedBehind()) {
+            return TASK_FRAGMENT_VISIBILITY_VISIBLE;
+        }
+
+        boolean gotRootSplitScreenFragment = false;
+        boolean gotOpaqueSplitScreenPrimary = false;
+        boolean gotOpaqueSplitScreenSecondary = false;
+        boolean gotTranslucentFullscreen = false;
+        boolean gotTranslucentSplitScreenPrimary = false;
+        boolean gotTranslucentSplitScreenSecondary = false;
+        boolean shouldBeVisible = true;
+
+        // This TaskFragment is only considered visible if all its parent TaskFragments are
+        // considered visible, so check the visibility of all ancestor TaskFragment first.
+        final WindowContainer parent = getParent();
+        if (parent.asTaskFragment() != null) {
+            final int parentVisibility = parent.asTaskFragment().getVisibility(starting);
+            if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
+                // Can't be visible if parent isn't visible
+                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+            } else if (parentVisibility == TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
+                // Parent is behind a translucent container so the highest visibility this container
+                // can get is that.
+                gotTranslucentFullscreen = true;
+            }
+        }
+
+        final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
+        final int windowingMode = getWindowingMode();
+        final boolean isAssistantType = isActivityTypeAssistant();
+        for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+            final WindowContainer other = parent.getChildAt(i);
+            if (other == null) continue;
+
+            final boolean hasRunningActivities = hasRunningActivity(other);
+            if (other == this) {
+                // Should be visible if there is no other fragment occluding it, unless it doesn't
+                // have any running activities, not starting one and not home stack.
+                shouldBeVisible = hasRunningActivities
+                        || (starting != null && starting.isDescendantOf(this))
+                        || isActivityTypeHome();
+                break;
+            }
+
+            if (!hasRunningActivities) {
+                continue;
+            }
+
+            final int otherWindowingMode = other.getWindowingMode();
+            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+                if (isTranslucent(other, starting)) {
+                    // Can be visible behind a translucent fullscreen TaskFragment.
+                    gotTranslucentFullscreen = true;
+                    continue;
+                }
+                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+            } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+                    && other.matchParentBounds()) {
+                if (isTranslucent(other, starting)) {
+                    // Can be visible behind a translucent TaskFragment.
+                    gotTranslucentFullscreen = true;
+                    continue;
+                }
+                // Multi-window TaskFragment that matches parent bounds would occlude other children
+                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    && !gotOpaqueSplitScreenPrimary) {
+                gotRootSplitScreenFragment = true;
+                gotTranslucentSplitScreenPrimary = isTranslucent(other, starting);
+                gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                        && gotOpaqueSplitScreenPrimary) {
+                    // Can't be visible behind another opaque TaskFragment in split-screen-primary.
+                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                }
+            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                    && !gotOpaqueSplitScreenSecondary) {
+                gotRootSplitScreenFragment = true;
+                gotTranslucentSplitScreenSecondary = isTranslucent(other, starting);
+                gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        && gotOpaqueSplitScreenSecondary) {
+                    // Can't be visible behind another opaque TaskFragment in split-screen-secondary
+                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                }
+            }
+            if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+                // Can not be visible if we are in split-screen windowing mode and both halves of
+                // the screen are opaque.
+                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+            }
+            if (isAssistantType && gotRootSplitScreenFragment) {
+                // Assistant TaskFragment can't be visible behind split-screen. In addition to
+                // this not making sense, it also works around an issue here we boost the z-order
+                // of the assistant window surfaces in window manager whenever it is visible.
+                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+            }
+
+            final TaskFragment otherTaskFrag = other.asTaskFragment();
+            if (otherTaskFrag != null && otherTaskFrag.mAdjacentTaskFragment != null) {
+                if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
+                    if (otherTaskFrag.isTranslucent(starting)
+                            || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
+                        // Can be visible behind a translucent adjacent TaskFragments.
+                        gotTranslucentFullscreen = true;
+                        continue;
+                    }
+                    // Can not be visible behind adjacent TaskFragments.
+                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                } else {
+                    adjacentTaskFragments.add(otherTaskFrag);
+                }
+            }
+
+        }
+
+        if (!shouldBeVisible) {
+            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+        }
+
+        // Handle cases when there can be a translucent split-screen TaskFragment on top.
+        switch (windowingMode) {
+            case WINDOWING_MODE_FULLSCREEN:
+                if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
+                    // At least one of the split-screen TaskFragment that covers this one is
+                    // translucent.
+                    // When in split mode, home will be reparented to the secondary split while
+                    // leaving TaskFragments not supporting split below. Due to
+                    // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+                    // the bottom, this makes sure TaskFragments not in split roots won't occlude
+                    // home task unexpectedly.
+                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                }
+                break;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                if (gotTranslucentSplitScreenPrimary) {
+                    // Covered by translucent primary split-screen on top.
+                    return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+                }
+                break;
+            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+                if (gotTranslucentSplitScreenSecondary) {
+                    // Covered by translucent secondary split-screen on top.
+                    return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+                }
+                break;
+        }
+
+        // Lastly - check if there is a translucent fullscreen TaskFragment on top.
+        return gotTranslucentFullscreen
+                ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
+                : TASK_FRAGMENT_VISIBILITY_VISIBLE;
+    }
+
+    private static boolean hasRunningActivity(WindowContainer wc) {
+        if (wc.asTaskFragment() != null) {
+            return wc.asTaskFragment().topRunningActivity() != null;
+        }
+        return wc.asActivityRecord() != null && !wc.asActivityRecord().finishing;
+    }
+
+    private static boolean isTranslucent(WindowContainer wc, ActivityRecord starting) {
+        if (wc.asTaskFragment() != null) {
+            return wc.asTaskFragment().isTranslucent(starting);
+        } else if (wc.asActivityRecord() != null) {
+            return !wc.asActivityRecord().occludesParent();
+        }
+        return false;
+    }
+
+
+    private boolean isTopActivityLaunchedBehind() {
+        final ActivityRecord top = topRunningActivity();
+        return top != null && top.mLaunchTaskBehind;
+    }
+
+    final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        mTaskSupervisor.beginActivityVisibilityUpdate();
+        try {
+            mEnsureActivitiesVisibleHelper.process(
+                    starting, configChanges, preserveWindows, notifyClients);
+        } finally {
+            mTaskSupervisor.endActivityVisibilityUpdate();
+        }
+    }
+
+    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
+            boolean deferPause) {
+        ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+        if (next == null || !next.canResumeByCompat()) {
+            return false;
+        }
+
+        next.delayedResume = false;
+        final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+        // If the top activity is the resumed one, nothing to do.
+        if (mResumedActivity == next && next.isState(RESUMED)
+                && taskDisplayArea.allResumedActivitiesComplete()) {
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            executeAppTransition(options);
+            // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+            // we still want to check if the visibility of other windows have changed (e.g. bringing
+            // a fullscreen window forward to cover another freeform activity.)
+            if (taskDisplayArea.inMultiWindowMode()) {
+                taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                        false /* preserveWindows */, true /* notifyClients */);
+            }
+            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+                    + "resumed %s", next);
+            return false;
+        }
+
+        // If we are currently pausing an activity, then don't do anything until that is done.
+        final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+        if (!allPausedComplete) {
+            ProtoLog.v(WM_DEBUG_STATES,
+                    "resumeTopActivity: Skip resume: some activity pausing.");
+            return false;
+        }
+
+        // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+        // well that is the state we want.
+        if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            executeAppTransition(options);
+            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and"
+                    + " all paused");
+            return false;
+        }
+
+        // Make sure that the user who owns this activity is started.  If not,
+        // we will just leave it as is because someone should be bringing
+        // another user's activities to the top of the stack.
+        if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
+            Slog.w(TAG, "Skipping resume of top activity " + next
+                    + ": user " + next.mUserId + " is stopped");
+            return false;
+        }
+
+        // The activity may be waiting for stop, but that is no longer
+        // appropriate for it.
+        mTaskSupervisor.mStoppingActivities.remove(next);
+
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+        mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+        ActivityRecord lastResumed = null;
+        final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
+        if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {
+            // So, why aren't we using prev here??? See the param comment on the method. prev
+            // doesn't represent the last resumed activity. However, the last focus stack does if
+            // it isn't null.
+            lastResumed = lastFocusedRootTask.getTopResumedActivity();
+        }
+
+        boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
+        if (mResumedActivity != null) {
+            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
+            pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
+                    next, "resumeTopActivity");
+        }
+        if (pausing) {
+            ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"
+                    + " start pausing");
+            // At this point we want to put the upcoming activity's process
+            // at the top of the LRU list, since we know we will be needing it
+            // very soon and it would be a waste to let it get killed if it
+            // happens to be sitting towards the end.
+            if (next.attachedToProcess()) {
+                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                        true /* activityChange */, false /* updateOomAdj */,
+                        false /* addPendingTopUid */);
+            } else if (!next.isProcessRunning()) {
+                // Since the start-process is asynchronous, if we already know the process of next
+                // activity isn't running, we can start the process earlier to save the time to wait
+                // for the current activity to be paused.
+                final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
+                mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+                        isTop ? "pre-top-activity" : "pre-activity");
+            }
+            if (lastResumed != null) {
+                lastResumed.setWillCloseOrEnterPip(true);
+            }
+            return true;
+        } else if (mResumedActivity == next && next.isState(RESUMED)
+                && taskDisplayArea.allResumedActivitiesComplete()) {
+            // It is possible for the activity to be resumed when we paused back stacks above if the
+            // next activity doesn't have to wait for pause to complete.
+            // So, nothing else to-do except:
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            executeAppTransition(options);
+            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed "
+                    + "(dontWaitForPause) %s", next);
+            return true;
+        }
+
+        // If the most recent activity was noHistory but was only stopped rather
+        // than stopped+finished because the device went to sleep, we need to make
+        // sure to finish it as we're making a new activity topmost.
+        if (shouldSleepActivities()) {
+            mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
+        }
+
+        if (prev != null && prev != next && next.nowVisible) {
+            // The next activity is already visible, so hide the previous
+            // activity's windows right now so we can show the new one ASAP.
+            // We only do this if the previous is finishing, which should mean
+            // it is on top of the one being resumed so hiding it quickly
+            // is good.  Otherwise, we want to do the normal route of allowing
+            // the resumed activity to be shown so we can decide if the
+            // previous should actually be hidden depending on whether the
+            // new one is found to be full-screen or not.
+            if (prev.finishing) {
+                prev.setVisibility(false);
+                if (DEBUG_SWITCH) {
+                    Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev
+                            + ", nowVisible=" + next.nowVisible);
+                }
+            } else {
+                if (DEBUG_SWITCH) {
+                    Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev
+                            + ", nowVisible=" + next.nowVisible);
+                }
+            }
+        }
+
+        // Launching this app's activity, make sure the app is no longer
+        // considered stopped.
+        try {
+            mTaskSupervisor.getActivityMetricsLogger()
+                    .notifyBeforePackageUnstopped(next.packageName);
+            mAtmService.getPackageManager().setPackageStoppedState(
+                    next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
+        } catch (RemoteException e1) {
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Failed trying to unstop package "
+                    + next.packageName + ": " + e);
+        }
+
+        // We are starting up the next activity, so tell the window manager
+        // that the previous one will be hidden soon.  This way it can know
+        // to ignore it when computing the desired screen orientation.
+        boolean anim = true;
+        final DisplayContent dc = taskDisplayArea.mDisplayContent;
+        if (prev != null) {
+            if (prev.finishing) {
+                if (DEBUG_TRANSITION) {
+                    Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
+                }
+                if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
+                    anim = false;
+                    dc.prepareAppTransition(TRANSIT_NONE);
+                } else {
+                    dc.prepareAppTransition(TRANSIT_CLOSE);
+                }
+                prev.setVisibility(false);
+            } else {
+                if (DEBUG_TRANSITION) {
+                    Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
+                }
+                if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+                    anim = false;
+                    dc.prepareAppTransition(TRANSIT_NONE);
+                } else {
+                    dc.prepareAppTransition(TRANSIT_OPEN,
+                            next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
+                }
+            }
+        } else {
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+            if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+                anim = false;
+                dc.prepareAppTransition(TRANSIT_NONE);
+            } else {
+                dc.prepareAppTransition(TRANSIT_OPEN);
+            }
+        }
+
+        if (anim) {
+            next.applyOptionsAnimation();
+        } else {
+            next.abortAndClearOptionsAnimation();
+        }
+
+        mTaskSupervisor.mNoAnimActivities.clear();
+
+        if (next.attachedToProcess()) {
+            if (DEBUG_SWITCH) {
+                Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped
+                        + " visibleRequested=" + next.mVisibleRequested);
+            }
+
+            // If the previous activity is translucent, force a visibility update of
+            // the next activity, so that it's added to WM's opening app list, and
+            // transition animation can be set up properly.
+            // For example, pressing Home button with a translucent activity in focus.
+            // Launcher is already visible in this case. If we don't add it to opening
+            // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+            // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+            final boolean lastActivityTranslucent = inMultiWindowMode()
+                    || mLastPausedActivity != null && !mLastPausedActivity.occludesParent();
+
+            // This activity is now becoming visible.
+            if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+                next.setVisibility(true);
+            }
+
+            // schedule launch ticks to collect information about slow apps.
+            next.startLaunchTickingLocked();
+
+            ActivityRecord lastResumedActivity =
+                    lastFocusedRootTask == null ? null
+                            : lastFocusedRootTask.getTopResumedActivity();
+            final ActivityRecord.State lastState = next.getState();
+
+            mAtmService.updateCpuStats();
+
+            ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
+
+            next.setState(RESUMED, "resumeTopActivity");
+
+            // Have the window manager re-evaluate the orientation of
+            // the screen based on the new activity order.
+            boolean notUpdated = true;
+
+            // Activity should also be visible if set mLaunchTaskBehind to true (see
+            // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
+            if (shouldBeVisible(next)) {
+                // We have special rotation behavior when here is some active activity that
+                // requests specific orientation or Keyguard is locked. Make sure all activity
+                // visibilities are set correctly as well as the transition is updated if needed
+                // to get the correct rotation behavior. Otherwise the following call to update
+                // the orientation may cause incorrect configurations delivered to client as a
+                // result of invisible window resize.
+                // TODO: Remove this once visibilities are set correctly immediately when
+                // starting an activity.
+                notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+                        true /* markFrozenIfConfigChanged */, false /* deferResume */);
+            }
+
+            if (notUpdated) {
+                // The configuration update wasn't able to keep the existing
+                // instance of the activity, and instead started a new one.
+                // We should be all done, but let's just make sure our activity
+                // is still at the top and schedule another run if something
+                // weird happened.
+                ActivityRecord nextNext = topRunningActivity();
+                ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
+                        + "%s, new next: %s", next, nextNext);
+                if (nextNext != next) {
+                    // Do over!
+                    mTaskSupervisor.scheduleResumeTopActivities();
+                }
+                if (!next.mVisibleRequested || next.stopped) {
+                    next.setVisibility(true);
+                }
+                next.completeResumeLocked();
+                return true;
+            }
+
+            try {
+                final ClientTransaction transaction =
+                        ClientTransaction.obtain(next.app.getThread(), next.appToken);
+                // Deliver all pending results.
+                ArrayList<ResultInfo> a = next.results;
+                if (a != null) {
+                    final int size = a.size();
+                    if (!next.finishing && size > 0) {
+                        if (DEBUG_RESULTS) {
+                            Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
+                        }
+                        transaction.addCallback(ActivityResultItem.obtain(a));
+                    }
+                }
+
+                if (next.newIntents != null) {
+                    transaction.addCallback(
+                            NewIntentItem.obtain(next.newIntents, true /* resume */));
+                }
+
+                // Well the app will no longer be stopped.
+                // Clear app token stopped state in window manager if needed.
+                next.notifyAppResumed(next.stopped);
+
+                EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
+                        next.getTask().mTaskId, next.shortComponentName);
+
+                mAtmService.getAppWarningsLocked().onResumeActivity(next);
+                next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+                next.abortAndClearOptionsAnimation();
+                transaction.setLifecycleStateRequest(
+                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
+                                dc.isNextTransitionForward()));
+                mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+
+                ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
+            } catch (Exception e) {
+                // Whoops, need to restart this activity!
+                ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
+                        + "%s", lastState, next);
+                next.setState(lastState, "resumeTopActivityInnerLocked");
+
+                // lastResumedActivity being non-null implies there is a lastStack present.
+                if (lastResumedActivity != null) {
+                    lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+                }
+
+                Slog.i(TAG, "Restarting because process died: " + next);
+                if (!next.hasBeenLaunched) {
+                    next.hasBeenLaunched = true;
+                } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+                        && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
+                    next.showStartingWindow(false /* taskSwitch */);
+                }
+                mTaskSupervisor.startSpecificActivity(next, true, false);
+                return true;
+            }
+
+            // From this point on, if something goes wrong there is no way
+            // to recover the activity.
+            try {
+                next.completeResumeLocked();
+            } catch (Exception e) {
+                // If any exception gets thrown, toss away this
+                // activity and try the next one.
+                Slog.w(TAG, "Exception thrown during resume of " + next, e);
+                next.finishIfPossible("resume-exception", true /* oomAdj */);
+                return true;
+            }
+        } else {
+            // Whoops, need to restart this activity!
+            if (!next.hasBeenLaunched) {
+                next.hasBeenLaunched = true;
+            } else {
+                if (SHOW_APP_STARTING_PREVIEW) {
+                    next.showStartingWindow(false /* taskSwich */);
+                }
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+            }
+            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
+            mTaskSupervisor.startSpecificActivity(next, true, true);
+        }
+
+        return true;
+    }
+
+    boolean shouldSleepOrShutDownActivities() {
+        return shouldSleepActivities() || mAtmService.mShuttingDown;
+    }
+
+    /**
+     * Returns true if the TaskFragment should be visible.
+     *
+     * @param starting The currently starting activity or null if there is none.
+     */
+    boolean shouldBeVisible(ActivityRecord starting) {
+        return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+    }
+
+    final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) {
+        return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
+    }
+
+    /**
+     * Start pausing the currently resumed activity.  It is an error to call this if there
+     * is already an activity being paused or there is no resumed activity.
+     *
+     * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+     * @param uiSleeping True if this is happening with the user interface going to sleep (the
+     * screen turning off).
+     * @param resuming The activity we are currently trying to resume or null if this is not being
+     *                 called as part of resuming the top activity, so we shouldn't try to instigate
+     *                 a resume here if not null.
+     * @param reason The reason of pausing the activity.
+     * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+     * it to tell us when it is done.
+     */
+    boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
+            String reason) {
+        if (!hasDirectChildActivities()) {
+            return false;
+        }
+
+        ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
+                mResumedActivity);
+
+        if (mPausingActivity != null) {
+            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+                    + " state=" + mPausingActivity.getState());
+            if (!shouldSleepActivities()) {
+                // Avoid recursion among check for sleep and complete pause during sleeping.
+                // Because activity will be paused immediately after resume, just let pause
+                // be completed by the order of activity paused from clients.
+                completePause(false, resuming);
+            }
+        }
+        ActivityRecord prev = mResumedActivity;
+
+        if (prev == null) {
+            if (resuming == null) {
+                Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
+            }
+            return false;
+        }
+
+        if (prev == resuming) {
+            Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+            return false;
+        }
+
+        ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
+        mPausingActivity = prev;
+        mLastPausedActivity = prev;
+        if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+            mTaskSupervisor.mNoHistoryActivities.add(prev);
+        }
+        prev.setState(PAUSING, "startPausingLocked");
+        prev.getTask().touchActiveTime();
+
+        mAtmService.updateCpuStats();
+
+        boolean pauseImmediately = false;
+        boolean shouldAutoPip = false;
+        if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
+            // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
+            // activity to be paused, while at the same time resuming the new resume activity
+            // only if the previous activity can't go into Pip since we want to give Pip
+            // activities a chance to enter Pip before resuming the next activity.
+            final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
+                    "shouldResumeWhilePausing", userLeaving);
+            if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
+                shouldAutoPip = true;
+            } else if (!lastResumedCanPip) {
+                pauseImmediately = true;
+            } else {
+                // The previous activity may still enter PIP even though it did not allow auto-PIP.
+            }
+        }
+
+        boolean didAutoPip = false;
+        if (prev.attachedToProcess()) {
+            if (shouldAutoPip) {
+                ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+                        + "directly: %s", prev);
+
+                didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
+                mPausingActivity = null;
+            } else {
+                ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+                try {
+                    EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+                            prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+                    mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+                            prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+                                    prev.configChangeFlags, pauseImmediately));
+                } catch (Exception e) {
+                    // Ignore exception, if process died other code will cleanup.
+                    Slog.w(TAG, "Exception thrown during pause", e);
+                    mPausingActivity = null;
+                    mLastPausedActivity = null;
+                    mTaskSupervisor.mNoHistoryActivities.remove(prev);
+                }
+            }
+        } else {
+            mPausingActivity = null;
+            mLastPausedActivity = null;
+            mTaskSupervisor.mNoHistoryActivities.remove(prev);
+        }
+
+        // If we are not going to sleep, we want to ensure the device is
+        // awake until the next activity is started.
+        if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
+            mTaskSupervisor.acquireLaunchWakelock();
+        }
+
+        // If already entered PIP mode, no need to keep pausing.
+        if (mPausingActivity != null && !didAutoPip) {
+            // Have the window manager pause its key dispatching until the new
+            // activity has started.  If we're pausing the activity just because
+            // the screen is being turned off and the UI is sleeping, don't interrupt
+            // key dispatch; the same activity will pick it up again on wakeup.
+            if (!uiSleeping) {
+                prev.pauseKeyDispatchingLocked();
+            } else {
+                ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
+            }
+
+            if (pauseImmediately) {
+                // If the caller said they don't want to wait for the pause, then complete
+                // the pause now.
+                completePause(false, resuming);
+                return false;
+
+            } else {
+                prev.schedulePauseTimeout();
+                return true;
+            }
+
+        } else {
+            // This activity either failed to schedule the pause or it entered PIP mode,
+            // so just treat it as being paused now.
+            ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
+            if (resuming == null) {
+                mRootWindowContainer.resumeFocusedTasksTopActivities();
+            }
+            return false;
+        }
+    }
+
+    @VisibleForTesting
+    void completePause(boolean resumeNext, ActivityRecord resuming) {
+        // Complete the pausing process of a pausing activity, so it doesn't make sense to
+        // operate on non-leaf tasks.
+        // warnForNonLeafTask("completePauseLocked");
+
+        ActivityRecord prev = mPausingActivity;
+        ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
+
+        if (prev != null) {
+            prev.setWillCloseOrEnterPip(false);
+            final boolean wasStopping = prev.isState(STOPPING);
+            prev.setState(PAUSED, "completePausedLocked");
+            if (prev.finishing) {
+                // We will update the activity visibility later, no need to do in
+                // completeFinishing(). Updating visibility here might also making the next
+                // activities to be resumed, and could result in wrong app transition due to
+                // lack of previous activity information.
+                ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
+                prev = prev.completeFinishing(false /* updateVisibility */,
+                        "completePausedLocked");
+            } else if (prev.hasProcess()) {
+                ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+                                + "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,
+                        prev.mVisibleRequested);
+                if (prev.deferRelaunchUntilPaused) {
+                    // Complete the deferred relaunch that was waiting for pause to complete.
+                    ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
+                    prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
+                } else if (wasStopping) {
+                    // We are also stopping, the stop request must have gone soon after the pause.
+                    // We can't clobber it, because the stop confirmation will not be handled.
+                    // We don't need to schedule another stop, we only need to let it happen.
+                    prev.setState(STOPPING, "completePausedLocked");
+                } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+                    // Clear out any deferred client hide we might currently have.
+                    prev.setDeferHidingClient(false);
+                    // If we were visible then resumeTopActivities will release resources before
+                    // stopping.
+                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
+                            "completePauseLocked");
+                }
+            } else {
+                ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
+                prev = null;
+            }
+            // It is possible the activity was freezing the screen before it was paused.
+            // In that case go ahead and remove the freeze this activity has on the screen
+            // since it is no longer visible.
+            if (prev != null) {
+                prev.stopFreezingScreenLocked(true /*force*/);
+            }
+            mPausingActivity = null;
+        }
+
+        if (resumeNext) {
+            final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+            if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
+                mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
+                        null /* targetOptions */);
+            } else {
+                // checkReadyForSleep();
+                final ActivityRecord top =
+                        topRootTask != null ? topRootTask.topRunningActivity() : null;
+                if (top == null || (prev != null && top != prev)) {
+                    // If there are no more activities available to run, do resume anyway to start
+                    // something. Also if the top activity on the root task is not the just paused
+                    // activity, we need to go ahead and resume it to ensure we complete an
+                    // in-flight app switch.
+                    mRootWindowContainer.resumeFocusedTasksTopActivities();
+                }
+            }
+        }
+
+        if (prev != null) {
+            prev.resumeKeyDispatchingLocked();
+        }
+
+        mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+
+        // Notify when the task stack has changed, but only if visibilities changed (not just
+        // focus). Also if there is an active root pinned task - we always want to notify it about
+        // task stack changes, because its positioning may depend on it.
+        if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
+                || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
+            mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
+            mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+        }
+    }
+
+    @Override
+    void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+        super.forAllTaskFragments(callback, traverseTopToBottom);
+        callback.accept(this);
+    }
+
+    @Override
+    void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+        final int count = mChildren.size();
+        boolean isLeafTaskFrag = true;
+        if (traverseTopToBottom) {
+            for (int i = count - 1; i >= 0; --i) {
+                final TaskFragment child = mChildren.get(i).asTaskFragment();
+                if (child != null) {
+                    isLeafTaskFrag = false;
+                    child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+                }
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                final TaskFragment child = mChildren.get(i).asTaskFragment();
+                if (child != null) {
+                    isLeafTaskFrag = false;
+                    child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+                }
+            }
+        }
+        if (isLeafTaskFrag) callback.accept(this);
+    }
+
+    @Override
+    boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+        boolean isLeafTaskFrag = true;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskFragment child = mChildren.get(i).asTaskFragment();
+            if (child != null) {
+                isLeafTaskFrag = false;
+                if (child.forAllLeafTaskFragments(callback)) {
+                    return true;
+                }
+            }
+        }
+        if (isLeafTaskFrag) {
+            return callback.apply(this);
+        }
+        return false;
+    }
+
+    void addChild(ActivityRecord r) {
+        addChild(r, POSITION_TOP);
+    }
+
+    @Override
+    void addChild(WindowContainer child, int index) {
+        boolean isAddingActivity = child.asActivityRecord() != null;
+        final Task task = isAddingActivity ? getTask() : null;
+
+        // If this task had any child before we added this one.
+        boolean taskHadChild = task != null && task.hasChild();
+        // getActivityType() looks at the top child, so we need to read the type before adding
+        // a new child in case the new child is on top and UNDEFINED.
+        final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+
+        super.addChild(child, index);
+
+        if (isAddingActivity && task != null) {
+            child.asActivityRecord().inHistory = true;
+            task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord());
+        }
+    }
+
+    void onChildPositionChanged(WindowContainer child) {
+        super.onChildPositionChanged(child);
+
+        sendTaskFragmentInfoChanged();
+    }
+
+    void executeAppTransition(ActivityOptions options) {
+        // No app transition applied to the task fragment.
+    }
+
+    boolean shouldSleepActivities() {
+        return false;
+    }
+
+    @Override
+    void resolveOverrideConfiguration(Configuration newParentConfig) {
+        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+        super.resolveOverrideConfiguration(newParentConfig);
+
+        int windowingMode =
+                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+        final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+
+        // Resolve override windowing mode to fullscreen for home task (even on freeform
+        // display), or split-screen if in split-screen mode.
+        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
+                    ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
+            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+        }
+
+        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+        // pinned windowing mode.
+        if (!supportsMultiWindow()) {
+            final int candidateWindowingMode =
+                    windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
+            if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
+                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
+                getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
+                        WINDOWING_MODE_FULLSCREEN);
+            }
+        }
+
+        final Task thisTask = asTask();
+        if (thisTask != null) {
+            thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
+                    mTmpBounds /* previousBounds */);
+        }
+        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+    }
+
+    boolean supportsMultiWindow() {
+        return supportsMultiWindowInDisplayArea(getDisplayArea());
+    }
+
+    /**
+     * @return whether this task supports multi-window if it is in the given
+     *         {@link TaskDisplayArea}.
+     */
+    boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+        if (!mAtmService.mSupportsMultiWindow) {
+            return false;
+        }
+        final Task task = getTask();
+        if (task == null) {
+            return false;
+        }
+        if (tda == null) {
+            Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi"
+                    + " window. Task id=" + getTaskId() + " attached=" + isAttached());
+            return false;
+        }
+        if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
+            // Not support non-resizable in multi window.
+            return false;
+        }
+
+        return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
+    }
+
+    private int getTaskId() {
+        return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID;
+    }
+
+    /**
+     * Ensures all visible activities at or below the input activity have the right configuration.
+     */
+    void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+        mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
+    }
+
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+            @NonNull Configuration parentConfig) {
+        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+                null /* compatInsets */);
+    }
+
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+            @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
+        if (overrideDisplayInfo != null) {
+            // Make sure the screen related configs can be computed by the provided display info.
+            inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+            invalidateAppBoundsConfig(inOutConfig);
+        }
+        computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
+                null /* compatInsets */);
+    }
+
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+            @NonNull Configuration parentConfig,
+            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+        if (compatInsets != null) {
+            // Make sure the app bounds can be computed by the compat insets.
+            invalidateAppBoundsConfig(inOutConfig);
+        }
+        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+                compatInsets);
+    }
+
+    /**
+     * Forces the app bounds related configuration can be computed by
+     * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+     * ActivityRecord.CompatDisplayInsets)}.
+     */
+    private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+        final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+        if (appBounds != null) {
+            appBounds.setEmpty();
+        }
+        inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+        inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+    }
+
+    /**
+     * Calculates configuration values used by the client to get resources. This should be run
+     * using app-facing bounds (bounds unmodified by animations or transient interactions).
+     *
+     * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+     * configuring an "inherit-bounds" window which means that all configuration settings would
+     * just be inherited from the parent configuration.
+     **/
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+            @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
+            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+        int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+        }
+
+        float density = inOutConfig.densityDpi;
+        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+            density = parentConfig.densityDpi;
+        }
+        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+        // The bounds may have been overridden at this level. If the parent cannot cover these
+        // bounds, the configuration is still computed according to the override bounds.
+        final boolean insideParentBounds;
+
+        final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+        final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
+        if (resolvedBounds == null || resolvedBounds.isEmpty()) {
+            mTmpFullBounds.set(parentBounds);
+            insideParentBounds = true;
+        } else {
+            mTmpFullBounds.set(resolvedBounds);
+            insideParentBounds = parentBounds.contains(resolvedBounds);
+        }
+
+        // Non-null compatibility insets means the activity prefers to keep its original size, so
+        // out bounds doesn't need to be restricted by the parent or current display
+        final boolean customContainerPolicy = compatInsets != null;
+
+        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+        if (outAppBounds == null || outAppBounds.isEmpty()) {
+            // App-bounds hasn't been overridden, so calculate a value for it.
+            inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
+            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+
+            if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
+                final Rect containingAppBounds;
+                if (insideParentBounds) {
+                    containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+                } else {
+                    // Restrict appBounds to display non-decor rather than parent because the
+                    // override bounds are beyond the parent. Otherwise, it won't match the
+                    // overridden bounds.
+                    final TaskDisplayArea displayArea = getDisplayArea();
+                    containingAppBounds = displayArea != null
+                            ? displayArea.getWindowConfiguration().getAppBounds() : null;
+                }
+                if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
+                    outAppBounds.intersect(containingAppBounds);
+                }
+            }
+        }
+
+        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+                || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+            if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
+                mTmpNonDecorBounds.set(mTmpFullBounds);
+                mTmpStableBounds.set(mTmpFullBounds);
+            } else if (!customContainerPolicy
+                    && (overrideDisplayInfo != null || getDisplayContent() != null)) {
+                final DisplayInfo di = overrideDisplayInfo != null
+                        ? overrideDisplayInfo
+                        : getDisplayContent().getDisplayInfo();
+
+                // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+                // area, i.e. the screen area without the system bars.
+                // The non decor inset are areas that could never be removed in Honeycomb. See
+                // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+                calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
+            } else {
+                // Apply the given non-decor and stable insets to calculate the corresponding bounds
+                // for screen size of configuration.
+                int rotation = inOutConfig.windowConfiguration.getRotation();
+                if (rotation == ROTATION_UNDEFINED) {
+                    rotation = parentConfig.windowConfiguration.getRotation();
+                }
+                if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
+                    mTmpNonDecorBounds.set(mTmpFullBounds);
+                    mTmpStableBounds.set(mTmpFullBounds);
+                    compatInsets.getBoundsByRotation(mTmpBounds, rotation);
+                    intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
+                            compatInsets.mNonDecorInsets[rotation]);
+                    intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
+                            compatInsets.mStableInsets[rotation]);
+                    outAppBounds.set(mTmpNonDecorBounds);
+                } else {
+                    // Set to app bounds because it excludes decor insets.
+                    mTmpNonDecorBounds.set(outAppBounds);
+                    mTmpStableBounds.set(outAppBounds);
+                }
+            }
+
+            if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+                final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
+                inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
+                        ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
+                        : overrideScreenWidthDp;
+            }
+            if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+                final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
+                inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
+                        ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
+                        : overrideScreenHeightDp;
+            }
+
+            if (inOutConfig.smallestScreenWidthDp
+                    == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+                if (WindowConfiguration.isFloating(windowingMode)) {
+                    // For floating tasks, calculate the smallest width from the bounds of the task
+                    inOutConfig.smallestScreenWidthDp = (int) (
+                            Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
+                }
+                // otherwise, it will just inherit
+            }
+        }
+
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+                    ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+        }
+        if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+            // For calculating screen layout, we need to use the non-decor inset screen area for the
+            // calculation for compatibility reasons, i.e. screen area without system bars that
+            // could never go away in Honeycomb.
+            int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+            int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+            // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+            // undefined so it can't be used.
+            if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+                compatScreenWidthDp = inOutConfig.screenWidthDp;
+            }
+            if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+                compatScreenHeightDp = inOutConfig.screenHeightDp;
+            }
+            // Reducing the screen layout starting from its parent config.
+            inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
+                    compatScreenWidthDp, compatScreenHeightDp);
+        }
+    }
+
+    /**
+     * Gets bounds with non-decor and stable insets applied respectively.
+     *
+     * If bounds overhangs the display, those edges will not get insets. See
+     * {@link #intersectWithInsetsIfFits}
+     *
+     * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+     * @param outStableBounds where to place bounds with stable insets applied.
+     * @param bounds the bounds to inset.
+     */
+    void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+            DisplayInfo displayInfo) {
+        outNonDecorBounds.set(bounds);
+        outStableBounds.set(bounds);
+        final Task rootTask = getRootTaskFragment().asTask();
+        if (rootTask == null || rootTask.mDisplayContent == null) {
+            return;
+        }
+        mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+        final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
+        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
+        intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+
+        policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+        intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+    }
+
+    /**
+     * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+     * intersectBounds on a side, then the respective side will not be intersected.
+     *
+     * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+     * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+     * bounds are larger than the provided parent/display bounds.
+     *
+     * @param inOutBounds the bounds to intersect.
+     * @param intersectBounds the bounds to intersect with.
+     * @param intersectInsets insets to apply to intersectBounds before intersecting.
+     */
+    static void intersectWithInsetsIfFits(
+            Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+        if (inOutBounds.right <= intersectBounds.right) {
+            inOutBounds.right =
+                    Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+        }
+        if (inOutBounds.bottom <= intersectBounds.bottom) {
+            inOutBounds.bottom =
+                    Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+        }
+        if (inOutBounds.left >= intersectBounds.left) {
+            inOutBounds.left =
+                    Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+        }
+        if (inOutBounds.top >= intersectBounds.top) {
+            inOutBounds.top =
+                    Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+        }
+    }
+
+    /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
+    static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
+            int screenHeightDp) {
+        sourceScreenLayout = sourceScreenLayout
+                & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+        final int longSize = Math.max(screenWidthDp, screenHeightDp);
+        final int shortSize = Math.min(screenWidthDp, screenHeightDp);
+        return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
+    }
+
+    @Override
+    public int getActivityType() {
+        final int applicationType = super.getActivityType();
+        if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
+            return applicationType;
+        }
+        return getTopChild().getActivityType();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        super.onConfigurationChanged(newParentConfig);
+        sendTaskFragmentInfoChanged();
+    }
+
+    @Override
+    void setSurfaceControl(SurfaceControl sc) {
+        super.setSurfaceControl(sc);
+        // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
+        // emit the callbacks now.
+        sendTaskFragmentAppeared();
+    }
+
+    void sendTaskFragmentInfoChanged() {
+        if (mTaskFragmentOrganizer != null) {
+            mTaskFragmentOrganizerController
+                    .onTaskFragmentInfoChanged(mTaskFragmentOrganizer, this);
+        }
+    }
+
+    private void sendTaskFragmentAppeared() {
+        if (mTaskFragmentOrganizer != null) {
+            mTaskFragmentOrganizerController.onTaskFragmentAppeared(mTaskFragmentOrganizer, this);
+        }
+    }
+
+    private void sendTaskFragmentVanished() {
+        if (mTaskFragmentOrganizer != null) {
+            mTaskFragmentOrganizerController.onTaskFragmentVanished(mTaskFragmentOrganizer, this);
+        }
+    }
+
+    /**
+     * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be
+     * called from {@link Task}.
+     */
+    TaskFragmentInfo getTaskFragmentInfo() {
+        List<IBinder> childActivities = new ArrayList<>();
+        for (int i = 0; i < getChildCount(); i++) {
+            WindowContainer wc = getChildAt(i);
+            if (mTaskFragmentOrganizerPid != ActivityRecord.INVALID_PID
+                    && wc.asActivityRecord() != null
+                    && wc.asActivityRecord().getPid() == mTaskFragmentOrganizerPid) {
+                // Only includes Activities that belong to the organizer process for security.
+                childActivities.add(wc.asActivityRecord().appToken);
+            }
+        }
+        final Point positionInParent = new Point();
+        getRelativePosition(positionInParent);
+        return new TaskFragmentInfo(
+                mFragmentToken,
+                mRemoteToken.toWindowContainerToken(),
+                getConfiguration(),
+                getChildCount() == 0,
+                isVisible(),
+                childActivities,
+                positionInParent);
+    }
+
+    @Nullable
+    IBinder getFragmentToken() {
+        return mFragmentToken;
+    }
+
+    @Nullable
+    @VisibleForTesting
+    ITaskFragmentOrganizer getTaskFragmentOrganizer() {
+        return mTaskFragmentOrganizer;
+    }
+
+    /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
+    void clearLastPausedActivity() {
+        forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
+    }
+
+    /**
+     * Sets {@link #mMinWidth} and {@link #mMinWidth} to this TaskFragment.
+     * It is usually set from the parent {@link Task} when adding the TaskFragment to the window
+     * hierarchy.
+     */
+    void setMinDimensions(int minWidth, int minHeight) {
+        if (asTask() != null) {
+            throw new UnsupportedOperationException("This method must not be used to Task. The "
+                    + " minimum dimension of Task should be passed from Task constructor.");
+        }
+        mMinWidth = minWidth;
+        mMinHeight = minHeight;
+    }
+
+    @Override
+    void removeImmediately() {
+        if (mRemoving) {
+            return;
+        }
+        mRemoving = true;
+        super.removeImmediately();
+        sendTaskFragmentVanished();
+        mRemoving = false;
+    }
+
+    boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+            boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
+        boolean printed = false;
+        Runnable headerPrinter = () -> {
+            if (needSep) {
+                pw.println();
+            }
+            if (header != null) {
+                header.run();
+            }
+
+            dumpInner(prefix, pw, dumpAll, dumpPackage);
+        };
+
+        if (dumpPackage == null) {
+            // If we are not filtering by package, we want to print absolutely everything,
+            // so always print the header even if there are no tasks/activities inside.
+            headerPrinter.run();
+            headerPrinter = null;
+            printed = true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            WindowContainer child = mChildren.get(i);
+            if (child.asTaskFragment() != null) {
+                printed |= child.asTaskFragment().dump(prefix + "      ", fd, pw, dumpAll,
+                        dumpClient, dumpPackage, needSep, headerPrinter);
+            } else if (child.asActivityRecord() != null) {
+                ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + "      ",
+                        "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter,
+                        getTask());
+            }
+        }
+
+        return printed;
+    }
+
+    void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+        pw.print(prefix); pw.print("* "); pw.println(this);
+        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
+        if (dumpAll) {
+            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+                    prefix + "  mLastPausedActivity: ", null);
+        }
+    }
+
+    @Override
+    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(HASH_CODE, System.identityHashCode(this));
+        final ActivityRecord topActivity = topRunningActivity();
+        proto.write(USER_ID, topActivity != null ? topActivity.mUserId : USER_NULL);
+        proto.write(TITLE, topActivity != null ? topActivity.intent.getComponent()
+                .flattenToShortString() : "TaskFragment");
+        proto.end(token);
+    }
+
+    @Override
+    long getProtoFieldId() {
+        return TASK_FRAGMENT;
+    }
+
+    @Override
+    public void dumpDebug(ProtoOutputStream proto, long fieldId,
+            @WindowTraceLogLevel int logLevel) {
+        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            return;
+        }
+
+        final long token = proto.start(fieldId);
+
+        super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+
+        proto.write(DISPLAY_ID, getDisplayId());
+        proto.write(ACTIVITY_TYPE, getActivityType());
+        proto.write(MIN_WIDTH, mMinWidth);
+        proto.write(MIN_HEIGHT, mMinHeight);
+
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
new file mode 100644
index 0000000..4843e5a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.window.TaskFragmentOrganizer.putExceptionInBundle;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.ITaskFragmentOrganizerController;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Stores and manages the client {@link android.window.TaskFragmentOrganizer}.
+ */
+public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub {
+    private static final String TAG = "TaskFragmentOrganizerController";
+
+    private final ActivityTaskManagerService mAtmService;
+    private final WindowManagerGlobalLock mGlobalLock;
+    /**
+     * A Map which manages the relationship between
+     * {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
+     */
+    private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
+            new ArrayMap<>();
+    /**
+     * A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
+     */
+    private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
+            new ArrayList<>();
+
+    TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
+        mAtmService = atm;
+        mGlobalLock = atm.mGlobalLock;
+    }
+
+    /**
+     * A class to manage {@link ITaskFragmentOrganizer} and its organized
+     * {@link TaskFragment TaskFragments}.
+     */
+    private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
+        private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+        private final ITaskFragmentOrganizer mOrganizer;
+        private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
+                new WeakHashMap<>();
+        private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
+                new WeakHashMap<>();
+
+        TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
+            mOrganizer = organizer;
+            try {
+                mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mGlobalLock) {
+                removeOrganizer(mOrganizer);
+            }
+        }
+
+        /**
+         * @return {@code true} if taskFragment is organized and not sent the appeared event before.
+         */
+        boolean addTaskFragment(TaskFragment taskFragment) {
+            if (taskFragment.mTaskFragmentAppearedSent) {
+                return false;
+            }
+            if (mOrganizedTaskFragments.contains(taskFragment)) {
+                return false;
+            }
+            mOrganizedTaskFragments.add(taskFragment);
+            return true;
+        }
+
+        void removeTaskFragment(TaskFragment taskFragment) {
+            mOrganizedTaskFragments.remove(taskFragment);
+        }
+
+        void dispose() {
+            mOrganizedTaskFragments.forEach(TaskFragment::removeImmediately);
+            mOrganizedTaskFragments.clear();
+            mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
+        }
+
+        void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
+            final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+            final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
+                    "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
+            try {
+                organizer.onTaskFragmentAppeared(
+                        new TaskFragmentAppearedInfo(info, outSurfaceControl));
+                mLastSentTaskFragmentInfos.put(tf, info);
+                tf.mTaskFragmentAppearedSent = true;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+            }
+            onTaskFragmentParentInfoChanged(organizer, tf);
+        }
+
+        void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
+            try {
+                organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskFragmentVanished callback", e);
+            }
+            tf.mTaskFragmentAppearedSent = false;
+            mLastSentTaskFragmentInfos.remove(tf);
+            mLastSentTaskFragmentParentConfigs.remove(tf);
+        }
+
+        void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+            // Parent config may have changed. The controller will check if there is any important
+            // config change for the organizer.
+            onTaskFragmentParentInfoChanged(organizer, tf);
+
+            // Check if the info is different from the last reported info.
+            final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+            final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
+            if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
+                    info.getConfiguration(), lastInfo.getConfiguration())) {
+                return;
+            }
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
+                    tf.getName());
+            try {
+                organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo());
+                mLastSentTaskFragmentInfos.put(tf, info);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+            }
+        }
+
+        void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+            // Check if the parent info is different from the last reported parent info.
+            if (tf.getParent() == null || tf.getParent().asTask() == null) {
+                mLastSentTaskFragmentParentConfigs.remove(tf);
+                return;
+            }
+            final Task parent = tf.getParent().asTask();
+            final Configuration parentConfig = parent.getConfiguration();
+            final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
+            if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)) {
+                return;
+            }
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "TaskFragment parent info changed name=%s parentTaskId=%d",
+                    tf.getName(), parent.mTaskId);
+            try {
+                organizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
+                mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
+            }
+        }
+
+        void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
+                Throwable exception) {
+            final Bundle exceptionBundle = putExceptionInBundle(exception);
+            try {
+                organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception sending onTaskFragmentError callback", e);
+            }
+        }
+    }
+
+    @Override
+    public void registerOrganizer(ITaskFragmentOrganizer organizer) {
+        final int pid = Binder.getCallingPid();
+        final long uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Register task fragment organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            if (mTaskFragmentOrganizerState.containsKey(organizer.asBinder())) {
+                throw new IllegalStateException(
+                        "Replacing existing organizer currently unsupported");
+            }
+            mTaskFragmentOrganizerState.put(organizer.asBinder(),
+                    new TaskFragmentOrganizerState(organizer));
+        }
+    }
+
+    @Override
+    public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
+        validateAndGetState(organizer);
+        final int pid = Binder.getCallingPid();
+        final long uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Unregister task fragment organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            removeOrganizer(organizer);
+        }
+    }
+
+    void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+        if (!state.addTaskFragment(taskFragment)) {
+            return;
+        }
+        PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
+                PendingTaskFragmentEvent.EVENT_APPEARED);
+        if (pendingEvent == null) {
+            pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer,
+                    PendingTaskFragmentEvent.EVENT_APPEARED);
+            mPendingTaskFragmentEvents.add(pendingEvent);
+        }
+    }
+
+    void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+        handleTaskFragmentInfoChanged(organizer, taskFragment,
+                PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
+    }
+
+    void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
+            TaskFragment taskFragment) {
+        handleTaskFragmentInfoChanged(organizer, taskFragment,
+                PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
+    }
+
+    private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
+            TaskFragment taskFragment, int eventType) {
+        validateAndGetState(organizer);
+        if (!taskFragment.mTaskFragmentAppearedSent) {
+            // Skip if TaskFragment still not appeared.
+            return;
+        }
+        PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
+        if (pendingEvent == null) {
+            pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer, eventType);
+        } else {
+            if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
+                // Skipped the info changed event if vanished event is pending.
+                return;
+            }
+            // Remove and add for re-ordering.
+            mPendingTaskFragmentEvents.remove(pendingEvent);
+        }
+        mPendingTaskFragmentEvents.add(pendingEvent);
+    }
+
+    void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+            if (taskFragment == entry.mTaskFragment) {
+                mPendingTaskFragmentEvents.remove(i);
+                if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
+                    // If taskFragment appeared callback is pending, ignore the vanished request.
+                    return;
+                }
+            }
+        }
+        if (!taskFragment.mTaskFragmentAppearedSent) {
+            return;
+        }
+        PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(taskFragment,
+                organizer, PendingTaskFragmentEvent.EVENT_VANISHED);
+        mPendingTaskFragmentEvents.add(pendingEvent);
+        state.removeTaskFragment(taskFragment);
+    }
+
+    void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
+            Throwable exception) {
+        validateAndGetState(organizer);
+        PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer,
+                errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR);
+        mPendingTaskFragmentEvents.add(pendingEvent);
+    }
+
+    private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+        final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+        // remove all of the children of the organized TaskFragment
+        state.dispose();
+        mTaskFragmentOrganizerState.remove(organizer.asBinder());
+    }
+
+    /**
+     * Makes sure that the organizer has been correctly registered to prevent any Sidecar
+     * implementation from organizing {@link TaskFragment} without registering first. In such case,
+     * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
+     * {@link TaskFragment} after the organizer process died.
+     */
+    private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+        final TaskFragmentOrganizerState state =
+                mTaskFragmentOrganizerState.get(organizer.asBinder());
+        if (state == null) {
+            throw new IllegalArgumentException(
+                    "TaskFragmentOrganizer has not been registered. Organizer=" + organizer);
+        }
+        return state;
+    }
+
+    /**
+     * A class to store {@link ITaskFragmentOrganizer} and its organized
+     * {@link TaskFragment TaskFragments} with different pending event request.
+     */
+    private static class PendingTaskFragmentEvent {
+        static final int EVENT_APPEARED = 0;
+        static final int EVENT_VANISHED = 1;
+        static final int EVENT_INFO_CHANGED = 2;
+        static final int EVENT_PARENT_INFO_CHANGED = 3;
+        static final int EVENT_ERROR = 4;
+
+        @IntDef(prefix = "EVENT_", value = {
+                EVENT_APPEARED,
+                EVENT_VANISHED,
+                EVENT_INFO_CHANGED,
+                EVENT_PARENT_INFO_CHANGED,
+                EVENT_ERROR
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface EventType {}
+
+        @EventType
+        private final int mEventType;
+        private final ITaskFragmentOrganizer mTaskFragmentOrg;
+        private final TaskFragment mTaskFragment;
+        private final IBinder mErrorCallback;
+        private final Throwable mException;
+
+        private PendingTaskFragmentEvent(TaskFragment taskFragment,
+                ITaskFragmentOrganizer taskFragmentOrg, @EventType int eventType) {
+            this(taskFragment, taskFragmentOrg, null /* errorCallback */,
+                    null /* exception */, eventType);
+
+        }
+
+        private PendingTaskFragmentEvent(ITaskFragmentOrganizer taskFragmentOrg,
+                IBinder errorCallback, Throwable exception, @EventType int eventType) {
+            this(null /* taskFragment */, taskFragmentOrg, errorCallback, exception,
+                    eventType);
+        }
+
+        private PendingTaskFragmentEvent(TaskFragment taskFragment,
+                ITaskFragmentOrganizer taskFragmentOrg, IBinder errorCallback, Throwable exception,
+                @EventType int eventType) {
+            mTaskFragment = taskFragment;
+            mTaskFragmentOrg = taskFragmentOrg;
+            mErrorCallback = errorCallback;
+            mException = exception;
+            mEventType = eventType;
+        }
+
+        /**
+         * @return {@code true} if the pending event is related with taskFragment created, vanished
+         * and information changed.
+         */
+        boolean isLifecycleEvent() {
+            switch (mEventType) {
+                case EVENT_APPEARED:
+                case EVENT_VANISHED:
+                case EVENT_INFO_CHANGED:
+                case EVENT_PARENT_INFO_CHANGED:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+    }
+
+    @Nullable
+    private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
+        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+            if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+            int type) {
+        for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+            PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+            if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    void dispatchPendingEvents() {
+        if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
+                || mPendingTaskFragmentEvents.isEmpty()) {
+            return;
+        }
+        for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
+            PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+            final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
+            final TaskFragment taskFragment = event.mTaskFragment;
+            final TaskFragmentOrganizerState state =
+                    mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
+            if (state == null) continue;
+            switch (event.mEventType) {
+                case PendingTaskFragmentEvent.EVENT_APPEARED:
+                    state.onTaskFragmentAppeared(taskFragmentOrg, taskFragment);
+                    break;
+                case PendingTaskFragmentEvent.EVENT_VANISHED:
+                    state.onTaskFragmentVanished(taskFragmentOrg, taskFragment);
+                    break;
+                case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
+                    state.onTaskFragmentInfoChanged(taskFragmentOrg, taskFragment);
+                    break;
+                case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
+                    state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment);
+                    break;
+                case PendingTaskFragmentEvent.EVENT_ERROR:
+                    state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback,
+                            event.mException);
+            }
+        }
+        mPendingTaskFragmentEvents.clear();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 09c5581..9ca89cb 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,15 +23,13 @@
 import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -69,13 +67,6 @@
 class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
 
-    /**
-     * Masks specifying which configurations are important to report back to an organizer when
-     * changed.
-     */
-    private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
-    private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
-
     // The set of modes that are currently supports
     // TODO: Remove once the task organizer can support all modes
     @VisibleForTesting
@@ -389,6 +380,15 @@
                                 mOrganizer.mTaskOrganizer, t);
                     }
                 }
+                if (mService.getTransitionController().isShellTransitionsEnabled()) {
+                    // dispose is only called outside of transitions (eg during unregister). Since
+                    // we "migrate" surfaces when replacing organizers, visibility gets delegated
+                    // to transitions; however, since there is no transition at this point, we have
+                    // to manually show the surface here.
+                    if (t.mTaskOrganizer != null && t.getSurfaceControl() != null) {
+                        t.getSyncTransaction().show(t.getSurfaceControl());
+                    }
+                }
             }
 
             // Remove organizer state after removing tasks so we get a chance to send
@@ -481,7 +481,8 @@
         final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mGlobalLock) {
+            final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+            final Runnable withGlobalLock = () -> {
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
                         organizer.asBinder(), uid);
                 if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
@@ -490,10 +491,10 @@
                             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())) {
+                    if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES,
+                            task.getWindowingMode())) {
                         return;
                     }
 
@@ -503,11 +504,19 @@
                     if (returnTask) {
                         SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
                                 "TaskOrganizerController.registerTaskOrganizer");
-                        taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+                        taskInfos.add(
+                                new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
                     }
                 });
-                return new ParceledListSlice<>(taskInfos);
+            };
+            if (mService.getTransitionController().isShellTransitionsEnabled()) {
+                mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+            } else {
+                synchronized (mGlobalLock) {
+                    withGlobalLock.run();
+                }
             }
+            return new ParceledListSlice<>(taskInfos);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -519,7 +528,7 @@
         final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mGlobalLock) {
+            final Runnable withGlobalLock = () -> {
                 final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
                 if (state == null) {
                     return;
@@ -528,6 +537,13 @@
                         organizer.asBinder(), uid);
                 state.unlinkDeath();
                 state.dispose();
+            };
+            if (mService.getTransitionController().isShellTransitionsEnabled()) {
+                mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+            } else {
+                synchronized (mGlobalLock) {
+                    withGlobalLock.run();
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -766,18 +782,9 @@
         mTmpTaskInfo.configuration.unset();
         task.fillTaskInfo(mTmpTaskInfo);
 
-        boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
-        if (!changed) {
-            int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
-            final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
-                    ? (int) mTmpTaskInfo.configuration.windowConfiguration.diff(
-                            lastInfo.configuration.windowConfiguration,
-                            true /* compareUndefined */) : 0;
-            if ((winCfgChanges & REPORT_WINDOW_CONFIGS) == 0) {
-                cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
-            }
-            changed = (cfgChanges & REPORT_CONFIGS) != 0;
-        }
+        boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo)
+                || !configurationsAreEqualForOrganizer(
+                        mTmpTaskInfo.configuration, lastInfo.configuration);
         if (!(changed || force)) {
             // mTmpTaskInfo will be reused next time.
             return;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e743710..c85615d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -118,6 +118,11 @@
      */
     private final boolean mIsRunningOnWear;
 
+    /**
+     * Flag indicating if device configuration has disabled app snapshots.
+     */
+    private final boolean mConfigDisableTaskSnapshots;
+
     TaskSnapshotController(WindowManagerService service) {
         mService = service;
         mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
@@ -131,6 +136,8 @@
             PackageManager.FEATURE_WATCH);
         mHighResTaskSnapshotScale = mService.mContext.getResources().getFloat(
                 com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+        mConfigDisableTaskSnapshots = mService.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableTaskSnapshots);
     }
 
     void systemReady() {
@@ -488,7 +495,8 @@
     }
 
     boolean shouldDisableSnapshots() {
-        return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
+        return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT
+                || mConfigDisableTaskSnapshots;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0cd09807..2c17b73 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,11 +16,13 @@
 
 package com.android.server.wm;
 
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_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;
@@ -32,19 +34,25 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArrayMap;
@@ -59,6 +67,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -124,8 +133,18 @@
     /** The final animation targets derived from participants after promotion. */
     private ArraySet<WindowContainer> mTargets = null;
 
+    /** Custom activity-level animation options and callbacks. */
+    private TransitionInfo.AnimationOptions mOverrideOptions;
+    private IRemoteCallback mClientAnimationStartCallback = null;
+    private IRemoteCallback mClientAnimationFinishCallback = null;
+
     private @TransitionState int mState = STATE_COLLECTING;
-    private boolean mReadyCalled = false;
+    private final ReadyTracker mReadyTracker = new ReadyTracker();
+
+    // TODO(b/188595497): remove when not needed.
+    /** @see RecentsAnimationController#mNavigationBarAttachedToApp */
+    private boolean mNavBarAttachedToApp = false;
+    private int mNavBarDisplayId = INVALID_DISPLAY;
 
     Transition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags,
             TransitionController controller, BLASTSyncEngine syncEngine) {
@@ -136,6 +155,10 @@
         mSyncId = mSyncEngine.startSyncSet(this);
     }
 
+    void addFlag(int flag) {
+        mFlags |= flag;
+    }
+
     @VisibleForTesting
     int getSyncId() {
         return mSyncId;
@@ -153,9 +176,7 @@
         mState = STATE_STARTED;
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
                 mSyncId);
-        if (mReadyCalled) {
-            setReady();
-        }
+        applyReady();
     }
 
     /**
@@ -170,6 +191,11 @@
         for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
                 curr = curr.getParent()) {
             mChanges.put(curr, new ChangeInfo(curr));
+            if (isReadyGroup(curr)) {
+                mReadyTracker.addGroup(curr);
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
+                                + " Transition %d with root=%s", mSyncId, curr);
+            }
         }
         if (mParticipants.contains(wc)) return;
         mSyncEngine.addToSyncSet(mSyncId, wc);
@@ -206,26 +232,60 @@
         mChanges.get(wc).mExistenceChanged = true;
     }
 
+    private void sendRemoteCallback(@Nullable IRemoteCallback callback) {
+        if (callback == null) return;
+        mController.mAtm.mH.sendMessage(PooledLambda.obtainMessage(cb -> {
+            try {
+                cb.sendResult(null);
+            } catch (RemoteException e) { }
+        }, callback));
+    }
+
+    /**
+     * Set animation options for collecting transition by ActivityRecord.
+     * @param options AnimationOptions captured from ActivityOptions
+     */
+    void setOverrideAnimation(TransitionInfo.AnimationOptions options,
+            @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
+        if (mSyncId < 0) return;
+        mOverrideOptions = options;
+        sendRemoteCallback(mClientAnimationStartCallback);
+        mClientAnimationStartCallback = startCallback;
+        mClientAnimationFinishCallback = finishCallback;
+    }
+
     /**
      * Call this when all known changes related to this transition have been applied. Until
      * all participants have finished drawing, the transition can still collect participants.
      *
      * If this is called before the transition is started, it will be deferred until start.
+     *
+     * @param wc A reference point to determine which ready-group to update. For now, each display
+     *           has its own ready-group, so this is used to look-up which display to mark ready.
+     *           The transition will wait for all groups to be ready.
      */
-    void setReady(boolean ready) {
+    void setReady(WindowContainer wc, boolean ready) {
         if (mSyncId < 0) return;
-        if (mState < STATE_STARTED) {
-            mReadyCalled = ready;
-            return;
-        }
+        mReadyTracker.setReadyFrom(wc, ready);
+        applyReady();
+    }
+
+    private void applyReady() {
+        if (mState < STATE_STARTED) return;
+        final boolean ready = mReadyTracker.allReady();
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                 "Set transition ready=%b %d", ready, mSyncId);
         mSyncEngine.setReady(mSyncId, ready);
     }
 
-    /** @see #setReady . This calls with parameter true. */
-    void setReady() {
-        setReady(true);
+    /**
+     * Sets all possible ready groups to ready.
+     * @see ReadyTracker#setAllReady.
+     */
+    void setAllReady() {
+        if (mSyncId < 0) return;
+        mReadyTracker.setAllReady();
+        applyReady();
     }
 
     /**
@@ -275,20 +335,56 @@
         }
 
         // Commit all going-invisible containers
+        boolean activitiesWentInvisible = false;
         for (int i = 0; i < mParticipants.size(); ++i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
-            if (ar != null && !ar.isVisibleRequested()) {
-                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "  Commit activity becoming invisible: %s", ar);
-                ar.commitVisibility(false /* visible */, false /* performLayout */);
+            if (ar != null) {
+                if (!ar.isVisibleRequested()) {
+                    boolean commitVisibility = true;
+                    if (ar.getDeferHidingClient() && ar.getTask() != null) {
+                        if (ar.pictureInPictureArgs != null
+                                && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
+                            mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs);
+                            // Avoid commit visibility to false here, or else we will get a sudden
+                            // "flash" / surface going invisible for a split second.
+                            commitVisibility = false;
+                        } else {
+                            mController.mAtm.mTaskSupervisor.mUserLeaving = true;
+                            ar.getTaskFragment().startPausing(false /* uiSleeping */,
+                                    null /* resuming */, "finishTransition");
+                            mController.mAtm.mTaskSupervisor.mUserLeaving = false;
+                        }
+                    }
+                    if (commitVisibility) {
+                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                                "  Commit activity becoming invisible: %s", ar);
+                        ar.commitVisibility(false /* visible */, false /* performLayout */);
+                        activitiesWentInvisible = true;
+                    }
+                }
+                if (mChanges.get(ar).mVisible != ar.isVisibleRequested()) {
+                    // Legacy dispatch relies on this (for now).
+                    ar.mEnteringAnimation = ar.isVisibleRequested();
+                }
+                mController.dispatchLegacyAppTransitionFinished(ar);
             }
             final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
             if (wt != null && !wt.isVisibleRequested()) {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "  Commit wallpaper becoming invisible: %s", ar);
+                        "  Commit wallpaper becoming invisible: %s", wt);
                 wt.commitVisibility(false /* visible */);
             }
         }
+        if (activitiesWentInvisible) {
+            // Always schedule stop processing when transition finishes because activities don't
+            // stop while they are in a transition thus their stop could still be pending.
+            mController.mAtm.mTaskSupervisor
+                    .scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+        }
+
+        sendRemoteCallback(mClientAnimationFinishCallback);
+
+        legacyRestoreNavigationBarFromApp();
     }
 
     void abort() {
@@ -297,6 +393,7 @@
         if (mState != STATE_COLLECTING) {
             throw new IllegalStateException("Too late to abort.");
         }
+        mController.dispatchLegacyAppTransitionCancelled();
         mState = STATE_ABORT;
         // Syncengine abort will call through to onTransactionReady()
         mSyncEngine.abort(mSyncId);
@@ -327,6 +424,7 @@
             mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
                     .getPendingTransaction().merge(transaction);
             mSyncId = -1;
+            mOverrideOptions = null;
             return;
         }
 
@@ -340,9 +438,18 @@
         // Resolve the animating targets from the participants
         mTargets = calculateTargets(mParticipants, mChanges);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
+        info.setAnimationOptions(mOverrideOptions);
+
+        // TODO(b/188669821): Move to animation impl in shell.
+        handleLegacyRecentsStartBehavior(displayId, info);
 
         handleNonAppWindowsInTransition(displayId, mType, mFlags);
 
+        reportStartReasonsToLogger();
+
+        // The callback is only populated for custom activity-level client animations
+        sendRemoteCallback(mClientAnimationStartCallback);
+
         // Manually show any activities that are visibleRequested. This is needed to properly
         // support simultaneous animation queueing/merging. Specifically, if transition A makes
         // an activity invisible, it's finishTransaction (which is applied *after* the animation)
@@ -354,12 +461,32 @@
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
             if (ar == null || !ar.mVisibleRequested) continue;
             transaction.show(ar.getSurfaceControl());
+
+            // Also manually show any non-reported parents. This is necessary in a few cases
+            // where a task is NOT organized but had its visibility changed within its direct
+            // parent. An example of this is if an alternate home leaf-task HB is started atop the
+            // normal home leaf-task HA: these are both in the Home root-task HR, so there will be a
+            // transition containing HA and HB where HA surface is hidden. If a standard task SA is
+            // launched on top, then HB finishes, no transition will happen since neither home is
+            // visible. When SA finishes, the transition contains HR rather than HA. Since home
+            // leaf-tasks are NOT organized, HA won't be in the transition and thus its surface
+            // wouldn't be shown. Just show is safe here since all other properties will have
+            // already been reset by the original hiding-transition's finishTransaction (we can't
+            // show in the finishTransaction because by then the activity doesn't hide until
+            // surface placement).
+            for (WindowContainer p = ar.getParent(); p != null && !mTargets.contains(p);
+                    p = p.getParent()) {
+                if (p.getSurfaceControl() != null) {
+                    transaction.show(p.getSurfaceControl());
+                }
+            }
         }
 
         mStartTransaction = transaction;
         mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
         buildFinishTransaction(mFinishTransaction, info.getRootLeash());
         if (mController.getTransitionPlayer() != null) {
+            mController.dispatchLegacyAppTransitionStarting(info);
             try {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                         "Calling onTransitionReady: %s", info);
@@ -375,6 +502,7 @@
             cleanUpOnFailure();
         }
         mSyncId = -1;
+        mOverrideOptions = null;
     }
 
     /**
@@ -394,6 +522,100 @@
         finishTransition();
     }
 
+    /** @see RecentsAnimationController#attachNavigationBarToApp */
+    private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+        if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
+            return;
+        }
+        final DisplayContent dc =
+                mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
+        if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+                // Skip the case where the nav bar is controlled by fade rotation.
+                || dc.getFadeRotationAnimationController() != null) {
+            return;
+        }
+
+        WindowContainer topWC = null;
+        // Find the top-most non-home, closing app.
+        for (int i = 0; i < info.getChanges().size(); ++i) {
+            final TransitionInfo.Change c = info.getChanges().get(i);
+            if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+                    || c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
+                    || !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
+                continue;
+            }
+            topWC = WindowContainer.fromBinder(c.getContainer().asBinder());
+            break;
+        }
+        if (topWC == null || topWC.inMultiWindowMode()) {
+            return;
+        }
+
+        final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+        if (navWindow == null || navWindow.mToken == null) {
+            return;
+        }
+        mNavBarAttachedToApp = true;
+        mNavBarDisplayId = displayId;
+        navWindow.mToken.cancelAnimation();
+        final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+        final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+        t.reparent(navSurfaceControl, topWC.getSurfaceControl());
+        t.show(navSurfaceControl);
+
+        final WindowContainer imeContainer = dc.getImeContainer();
+        if (imeContainer.isVisible()) {
+            t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
+        } else {
+            // Place the nav bar on top of anything else in the top activity.
+            t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
+        }
+        if (mController.mStatusBar != null) {
+            mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+        }
+    }
+
+    /** @see RecentsAnimationController#restoreNavigationBarFromApp */
+    void legacyRestoreNavigationBarFromApp() {
+        if (!mNavBarAttachedToApp) return;
+        mNavBarAttachedToApp = false;
+
+        if (mController.mStatusBar != null) {
+            mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true);
+        }
+
+        final DisplayContent dc =
+                mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId);
+        final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+        if (navWindow == null) return;
+        navWindow.setSurfaceTranslationY(0);
+
+        final WindowToken navToken = navWindow.mToken;
+        if (navToken == null) return;
+        final SurfaceControl.Transaction t = dc.getPendingTransaction();
+        final WindowContainer parent = navToken.getParent();
+        t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
+
+        boolean animate = false;
+        // Search for the home task. If it is supposed to be visible, then the navbar is not at
+        // the bottom of the screen, so we need to animate it.
+        for (int i = 0; i < mTargets.size(); ++i) {
+            final Task task = mTargets.valueAt(i).asTask();
+            if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+            animate = task.isVisibleRequested();
+            break;
+        }
+
+        if (animate) {
+            final NavBarFadeAnimationController controller =
+                    new NavBarFadeAnimationController(dc);
+            controller.fadeWindowToken(true);
+        } else {
+            // Reparent the SurfaceControl of nav bar token back.
+            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+        }
+    }
+
     private void handleNonAppWindowsInTransition(int displayId,
             @WindowManager.TransitionType int transit, int flags) {
         final DisplayContent dc =
@@ -427,6 +649,23 @@
         }
     }
 
+    private void reportStartReasonsToLogger() {
+        // Record transition start in metrics logger. We just assume everything is "DRAWN"
+        // at this point since splash-screen is a presentation (shell) detail.
+        ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
+        for (int i = mParticipants.size() - 1; i >= 0; --i) {
+            ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
+            if (r == null) continue;
+            // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
+            // ready due to starting-window.
+            reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
+                    && !r.mLastAllReadyAtSync)
+                    ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+        }
+        mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+                reasons);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(64);
@@ -612,7 +851,11 @@
         // of participants that should always be reported even if they aren't top.
         for (WindowContainer wc : participants) {
             // Don't include detached windows.
-            if (!wc.isAttached()) continue;
+            if (!wc.isAttached()) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "  Rejecting as detached: %s", wc);
+                continue;
+            }
 
             final ChangeInfo changeInfo = changes.get(wc);
 
@@ -629,6 +872,8 @@
             if (reportIfNotTop(wc)) {
                 tmpList.add(wc);
             }
+            // Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
+            boolean skipIntermediateReports = isWallpaper(wc);
             for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
                 if (!p.isAttached() || !changes.get(p).hasChanged(p)) {
                     // Again, we're skipping no-ops
@@ -637,7 +882,9 @@
                 if (participants.contains(p)) {
                     topParent = p;
                     break;
-                } else if (reportIfNotTop(p)) {
+                } else if (isWallpaper(p)) {
+                    skipIntermediateReports = true;
+                } else if (reportIfNotTop(p) && !skipIntermediateReports) {
                     tmpList.add(p);
                 }
             }
@@ -707,6 +954,15 @@
     }
 
     /**
+     * A ready group is defined by a root window-container where all transitioning windows under
+     * it are expected to animate together as a group. At the moment, this treats each display as
+     * a ready-group to match the existing legacy transition behavior.
+     */
+    private static boolean isReadyGroup(WindowContainer wc) {
+        return wc instanceof DisplayContent;
+    }
+
+    /**
      * Construct a TransitionInfo object from a set of targets and changes. Also populates the
      * root surface.
      */
@@ -723,17 +979,11 @@
         }
 
         // Find the top-most shared ancestor of app targets
-        WindowContainer ancestor = null;
-        for (int i = appTargets.size() - 1; i >= 0; --i) {
-            final WindowContainer wc = appTargets.valueAt(i);
-            ancestor = wc;
-            break;
-        }
-        if (ancestor == null) {
+        if (appTargets.isEmpty()) {
             out.setRootLeash(new SurfaceControl(), 0, 0);
             return out;
         }
-        ancestor = ancestor.getParent();
+        WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
 
         // Go up ancestor parent chain until all targets are descendants.
         ancestorLoop:
@@ -805,6 +1055,10 @@
         return out;
     }
 
+    boolean getLegacyIsReady() {
+        return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
+    }
+
     static Transition fromBinder(IBinder binder) {
         return (Transition) binder;
     }
@@ -891,6 +1145,10 @@
                     flags |= FLAG_IS_VOICE_INTERACTION;
                 }
             }
+            final DisplayContent dc = wc.asDisplayContent();
+            if (dc != null) {
+                flags |= FLAG_IS_DISPLAY;
+            }
             if (isWallpaper(wc)) {
                 flags |= FLAG_IS_WALLPAPER;
             }
@@ -910,4 +1168,95 @@
             mChildren.addAll(wcs);
         }
     }
+
+    /**
+     * The transition sync mechanism has 2 parts:
+     *   1. Whether all WM operations for a particular transition are "ready" (eg. did the app
+     *      launch or stop or get a new configuration?).
+     *   2. Whether all the windows involved have finished drawing their final-state content.
+     *
+     * A transition animation can play once both parts are complete. This ready-tracker keeps track
+     * of part (1). Currently, WM code assumes that "readiness" (part 1) is grouped. This means that
+     * even if the WM operations in one group are ready, the whole transition itself may not be
+     * ready if there are WM operations still pending in another group. This class helps keep track
+     * of readiness across the multiple groups. Currently, we assume that each display is a group
+     * since that is how it has been until now.
+     */
+    private static class ReadyTracker {
+        private final ArrayMap<WindowContainer, Boolean> mReadyGroups = new ArrayMap<>();
+
+        /**
+         * Ensures that this doesn't report as allReady before it has been used. This is needed
+         * in very niche cases where a transition is a no-op (nothing has been collected) but we
+         * still want to be marked ready (via. setAllReady).
+         */
+        private boolean mUsed = false;
+
+        /**
+         * If true, this overrides all ready groups and reports ready. Used by shell-initiated
+         * transitions via {@link #setAllReady()}.
+         */
+        private boolean mReadyOverride = false;
+
+        /**
+         * Adds a ready-group. Any setReady calls in this subtree will be tracked together. For
+         * now these are only DisplayContents.
+         */
+        void addGroup(WindowContainer wc) {
+            if (mReadyGroups.containsKey(wc)) {
+                Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
+                return;
+            }
+            mReadyGroups.put(wc, false);
+        }
+
+        /**
+         * Sets a group's ready state.
+         * @param wc Any container within a group's subtree. Used to identify the ready-group.
+         */
+        void setReadyFrom(WindowContainer wc, boolean ready) {
+            mUsed = true;
+            WindowContainer current = wc;
+            while (current != null) {
+                if (isReadyGroup(current)) {
+                    mReadyGroups.put(current, ready);
+                    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting Ready-group to"
+                            + " %b. group=%s from %s", ready, current, wc);
+                    break;
+                }
+                current = current.getParent();
+            }
+        }
+
+        /** Marks this as ready regardless of individual groups. */
+        void setAllReady() {
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting allReady override");
+            mUsed = true;
+            mReadyOverride = true;
+        }
+
+        /** @return true if all tracked subtrees are ready. */
+        boolean allReady() {
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b "
+                    + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString());
+            if (!mUsed) return false;
+            if (mReadyOverride) return true;
+            for (int i = mReadyGroups.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = mReadyGroups.keyAt(i);
+                if (!wc.isAttached() || !wc.isVisibleRequested()) continue;
+                if (!mReadyGroups.valueAt(i)) return false;
+            }
+            return true;
+        }
+
+        private String groupsToString() {
+            StringBuilder b = new StringBuilder();
+            for (int i = 0; i < mReadyGroups.size(); ++i) {
+                if (i != 0) b.append(',');
+                b.append(mReadyGroups.keyAt(i)).append(':')
+                        .append(mReadyGroups.valueAt(i));
+            }
+            return b.toString();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cc63c49..44dee4d 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,21 +17,34 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_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;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.WindowManager;
 import android.window.IRemoteTransition;
 import android.window.ITransitionPlayer;
+import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.util.ArrayList;
 
@@ -41,15 +54,25 @@
 class TransitionController {
     private static final String TAG = "TransitionController";
 
+    // State constants to line-up with legacy app-transition proto expectations.
+    private static final int LEGACY_STATE_IDLE = 0;
+    private static final int LEGACY_STATE_READY = 1;
+    private static final int LEGACY_STATE_RUNNING = 2;
+
     private ITransitionPlayer mTransitionPlayer;
     final ActivityTaskManagerService mAtm;
 
+    private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
+            new ArrayList<>();
+
     /**
      * Currently playing transitions (in the order they were started). When finished, records are
      * removed from this list.
      */
     private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
 
+    final Lock mRunningLock = new Lock();
+
     private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
         // clean-up/finish any playing transitions.
         for (int i = 0; i < mPlayingTransitions.size(); ++i) {
@@ -57,13 +80,18 @@
         }
         mPlayingTransitions.clear();
         mTransitionPlayer = null;
+        mRunningLock.doNotifyLocked();
     };
 
     /** The transition currently being constructed (collecting participants). */
     private Transition mCollectingTransition = null;
 
+    // TODO(b/188595497): remove when not needed.
+    final StatusBarManagerInternal mStatusBar;
+
     TransitionController(ActivityTaskManagerService atm) {
         mAtm = atm;
+        mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
     }
 
     /** @see #createTransition(int, int) */
@@ -76,7 +104,7 @@
      * Creates a transition. It can immediately collect participants.
      */
     @NonNull
-    Transition createTransition(@WindowManager.TransitionType int type,
+    private Transition createTransition(@WindowManager.TransitionType int type,
             @WindowManager.TransitionFlags int flags) {
         if (mTransitionPlayer == null) {
             throw new IllegalStateException("Shell Transitions not enabled");
@@ -87,6 +115,7 @@
         mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
                 mCollectingTransition);
+        dispatchLegacyAppTransitionPending();
         return mCollectingTransition;
     }
 
@@ -154,13 +183,9 @@
         return false;
     }
 
-    /**
-     * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
-     */
-    @Nullable
-    Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
-            @Nullable WindowContainer trigger) {
-        return requestTransitionIfNeeded(type, 0 /* flags */, trigger);
+    @WindowManager.TransitionType
+    int getCollectingTransitionType() {
+        return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
     }
 
     /**
@@ -168,8 +193,19 @@
      */
     @Nullable
     Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
-            @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
-        return requestTransitionIfNeeded(type, flags, trigger, null /* remote */);
+            @NonNull WindowContainer trigger) {
+        return requestTransitionIfNeeded(type, 0 /* flags */, trigger, trigger /* readyGroupRef */);
+    }
+
+    /**
+     * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
+     */
+    @Nullable
+    Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
+            @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
+            @NonNull WindowContainer readyGroupRef) {
+        return requestTransitionIfNeeded(type, flags, trigger, readyGroupRef,
+                null /* remoteTransition */);
     }
 
     private static boolean isExistenceType(@WindowManager.TransitionType int type) {
@@ -180,19 +216,20 @@
      * If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to
      * start it. Collection can start immediately.
      * @param trigger if non-null, this is the first container that will be collected
+     * @param readyGroupRef Used to identify which ready-group this request is for.
      * @return the created transition if created or null otherwise.
      */
     @Nullable
     Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
             @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
-            @Nullable IRemoteTransition remoteTransition) {
+            @NonNull WindowContainer readyGroupRef, @Nullable IRemoteTransition remoteTransition) {
         if (mTransitionPlayer == null) {
             return null;
         }
         Transition newTransition = null;
         if (isCollecting()) {
             // Make the collecting transition wait until this request is ready.
-            mCollectingTransition.setReady(false);
+            mCollectingTransition.setReady(readyGroupRef, false);
         } else {
             newTransition = requestStartTransition(createTransition(type, flags),
                     trigger != null ? trigger.asTask() : null, remoteTransition);
@@ -240,15 +277,22 @@
         mCollectingTransition.collectExistenceChange(wc);
     }
 
-    /** @see Transition#setReady */
-    void setReady(boolean ready) {
+    /** @see Transition#setOverrideAnimation */
+    void setOverrideAnimation(TransitionInfo.AnimationOptions options,
+            @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
         if (mCollectingTransition == null) return;
-        mCollectingTransition.setReady(ready);
+        mCollectingTransition.setOverrideAnimation(options, startCallback, finishCallback);
     }
 
     /** @see Transition#setReady */
-    void setReady() {
-        setReady(true);
+    void setReady(WindowContainer wc, boolean ready) {
+        if (mCollectingTransition == null) return;
+        mCollectingTransition.setReady(wc, ready);
+    }
+
+    /** @see Transition#setReady */
+    void setReady(WindowContainer wc) {
+        setReady(wc, true);
     }
 
     /** @see Transition#finishTransition */
@@ -261,6 +305,7 @@
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
         mPlayingTransitions.remove(record);
         record.finishTransition();
+        mRunningLock.doNotifyLocked();
     }
 
     void moveToPlaying(Transition transition) {
@@ -279,4 +324,109 @@
         mCollectingTransition = null;
     }
 
+    /**
+     * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
+     * behaviors.
+     * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+     */
+    void setIsLegacyRecents() {
+        if (mCollectingTransition == null) return;
+        mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+    }
+
+    void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
+        final Transition transition = Transition.fromBinder(token);
+        if (transition == null || !mPlayingTransitions.contains(transition)) {
+            Slog.e(TAG, "Transition isn't playing: " + token);
+            return;
+        }
+        transition.legacyRestoreNavigationBarFromApp();
+    }
+
+    void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) {
+        mLegacyListeners.add(listener);
+    }
+
+    void dispatchLegacyAppTransitionPending() {
+        for (int i = 0; i < mLegacyListeners.size(); ++i) {
+            mLegacyListeners.get(i).onAppTransitionPendingLocked();
+        }
+    }
+
+    void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
+        final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY
+                || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE
+                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION
+                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER
+                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0;
+        for (int i = 0; i < mLegacyListeners.size(); ++i) {
+            mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+                    0 /* durationHint */, SystemClock.uptimeMillis(),
+                    AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+        }
+    }
+
+    void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
+        for (int i = 0; i < mLegacyListeners.size(); ++i) {
+            mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+        }
+    }
+
+    void dispatchLegacyAppTransitionCancelled() {
+        for (int i = 0; i < mLegacyListeners.size(); ++i) {
+            mLegacyListeners.get(i).onAppTransitionCancelledLocked(
+                    false /* keyguardGoingAway */);
+        }
+    }
+
+    void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        int state = LEGACY_STATE_IDLE;
+        if (!mPlayingTransitions.isEmpty()) {
+            state = LEGACY_STATE_RUNNING;
+        } else if (mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) {
+            state = LEGACY_STATE_READY;
+        }
+        proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
+        proto.end(token);
+    }
+
+    class Lock {
+        private int mTransitionWaiters = 0;
+        void runWhenIdle(long timeout, Runnable r) {
+            synchronized (mAtm.mGlobalLock) {
+                if (!inTransition()) {
+                    r.run();
+                    return;
+                }
+                mTransitionWaiters += 1;
+            }
+            final long startTime = SystemClock.uptimeMillis();
+            final long endTime = startTime + timeout;
+            while (true) {
+                synchronized (mAtm.mGlobalLock) {
+                    if (!inTransition() || SystemClock.uptimeMillis() > endTime) {
+                        mTransitionWaiters -= 1;
+                        r.run();
+                        return;
+                    }
+                }
+                synchronized (this) {
+                    try {
+                        this.wait(timeout);
+                    } catch (InterruptedException e) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        void doNotifyLocked() {
+            synchronized (this) {
+                if (mTransitionWaiters > 0) {
+                    this.notifyAll();
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b1c7e19..ffee0b7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -851,7 +851,8 @@
     }
 
     boolean isAttached() {
-        return getDisplayArea() != null;
+        WindowContainer parent = getParent();
+        return parent != null && parent.isAttached();
     }
 
     void setWaitingForDrawnIfResizingChanged() {
@@ -1671,6 +1672,15 @@
         return false;
     }
 
+    boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).forAllLeafTaskFragments(callback)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * For all root tasks at or below this container call the callback.
      *
@@ -1726,6 +1736,28 @@
         }
     }
 
+    /**
+     * For all task fragments at or below this container call the callback.
+     *
+     * @param callback Callback to be called for every task.
+     */
+    void forAllTaskFragments(Consumer<TaskFragment> callback) {
+        forAllTaskFragments(callback, true /*traverseTopToBottom*/);
+    }
+
+    void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+        final int count = mChildren.size();
+        if (traverseTopToBottom) {
+            for (int i = count - 1; i >= 0; --i) {
+                mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+            }
+        }
+    }
+
     void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
         final int count = mChildren.size();
         if (traverseTopToBottom) {
@@ -1739,6 +1771,19 @@
         }
     }
 
+    void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+        final int count = mChildren.size();
+        if (traverseTopToBottom) {
+            for (int i = count - 1; i >= 0; --i) {
+                mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+            }
+        }
+    }
+
     /**
      * For all root tasks at or below this container call the callback.
      *
@@ -3075,6 +3120,11 @@
     }
 
     /** Cheap way of doing cast and instanceof. */
+    TaskFragment asTaskFragment() {
+        return null;
+    }
+
+    /** Cheap way of doing cast and instanceof. */
     WindowToken asWindowToken() {
         return null;
     }
@@ -3282,6 +3332,20 @@
     }
 
     /**
+     * Special helper to check that all windows are synced (vs just top one). This is only
+     * used to differentiate between starting-window vs full-drawn in activity-metrics reporting.
+     */
+    boolean allSyncFinished() {
+        if (!isVisibleRequested()) return true;
+        if (mSyncState != SYNC_STATE_READY) return false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (!child.allSyncFinished()) 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.
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index ffd6d21..baea854 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -89,6 +89,11 @@
     final Rect mCompatFrame = new Rect();
 
     /**
+     * {@code true} if the window frame is a simulated frame and attached to a decor window.
+     */
+    boolean mIsSimulatingDecorWindow = false;
+
+    /**
      * Whether the parent frame would have been different if there was no display cutout.
      */
     private boolean mParentFrameWasClippedByDisplayCutout;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 47087cf..2a271ef 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -40,6 +40,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Window manager local system service interface.
@@ -54,17 +55,18 @@
      */
     public interface AccessibilityControllerInternal {
         /**
-         * Enable the accessibility trace logging.
+         * Start tracing for the given logging types.
+         * @param loggingTypeFlags flags of the logging types enabled.
          */
-        void startTrace();
+        void startTrace(long loggingTypeFlags);
 
         /**
-         * Disable the accessibility trace logging.
+         * Disable accessibility tracing for all logging types.
          */
         void stopTrace();
 
         /**
-         * Is trace enabled or not.
+         * Is tracing enabled for any logging type.
          */
         boolean isAccessibilityTracingEnabled();
 
@@ -73,20 +75,23 @@
          *
          * @param where A string to identify this log entry, which can be used to filter/search
          *        through the tracing file.
+         * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
          * @param callingParams The parameters for the method to be logged.
          * @param a11yDump The proto byte array for a11y state when the entry is generated.
          * @param callingUid The calling uid.
          * @param stackTrace The stack trace, null if not needed.
+         * @param ignoreStackEntries The stack entries can be removed
          */
         void logTrace(
-                String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] stackTrace);
+                String where, long loggingTypeFlags, String callingParams, byte[] a11yDump,
+                int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries);
 
         /**
          * Add an accessibility trace entry.
          *
          * @param where A string to identify this log entry, which can be used to filter/search
          *        through the tracing file.
+         * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
          * @param callingParams The parameters for the method to be logged.
          * @param a11yDump The proto byte array for a11y state when the entry is generated.
          * @param callingUid The calling uid.
@@ -94,9 +99,11 @@
          * @param timeStamp The time when the method to be logged is called.
          * @param processId The calling process Id.
          * @param threadId The calling thread Id.
+         * @param ignoreStackEntries The stack entries can be removed
          */
-        void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid,
-                StackTraceElement[] callStack, long timeStamp, int processId, long threadId);
+        void logTrace(String where, long loggingTypeFlags, String callingParams,
+                byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp,
+                int processId, long threadId, Set<String> ignoreStackEntries);
     }
 
     /**
@@ -143,11 +150,11 @@
         void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
 
         /**
-         * Notifies that the rotation changed.
+         * Notifies that the display size is changed when rotation or the
+         * logical display is changed.
          *
-         * @param rotation The current rotation.
          */
-        void onRotationChanged(int rotation);
+        void onDisplaySizeChanged();
 
         /**
          * Notifies that the context of the user changed. For example, an application
@@ -358,9 +365,8 @@
      *
      * @param displayId The logical display id.
      * @param callback The callback.
-     * @return {@code false} if display id is not valid.
      */
-    public abstract boolean setWindowsForAccessibilityCallback(int displayId,
+    public abstract void setWindowsForAccessibilityCallback(int displayId,
             WindowsForAccessibilityCallback callback);
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1ec9187..9cb5126 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -47,6 +47,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -1804,7 +1805,8 @@
             winAnimator.mEnterAnimationPending = true;
             winAnimator.mEnteringAnimation = true;
             // Check if we need to prepare a transition for replacing window first.
-            if (activity != null && activity.isVisible()
+            if (mAtmService.getTransitionController().getTransitionPlayer() == null
+                    && activity != null && activity.isVisible()
                     && !prepareWindowReplacementTransition(activity)) {
                 // If not, check if need to set up a dummy transition during display freeze
                 // so that the unfreeze wait for the apps to draw. This might be needed if
@@ -2478,7 +2480,7 @@
             if (win.mActivityRecord != null) {
                 win.mActivityRecord.updateReportedVisibilityLocked();
             }
-            if (displayPolicy.areSystemBarsForcedShownLw(win)) {
+            if (displayPolicy.areSystemBarsForcedShownLw()) {
                 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
             }
             if (!win.isGoneForLayout()) {
@@ -2524,7 +2526,8 @@
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
             if (winAnimator.mSurfaceController != null) {
-                win.calculateSurfaceBounds(win.getAttrs(), mTmpRect);
+                win.calculateSurfaceBounds(win.getLayoutingAttrs(
+                        win.getWindowConfiguration().getRotation()), mTmpRect);
                 outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
             }
             getInsetsSourceControls(win, outActiveControls);
@@ -2717,8 +2720,8 @@
     }
 
     @Override
-    public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
-            Bundle options) {
+    public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
+            type, int displayId, Bundle options) {
         final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
                 "attachWindowContextToDisplayArea", false /* printLog */);
         final int callingUid = Binder.getCallingUid();
@@ -2729,15 +2732,17 @@
                 if (dc == null) {
                     ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
                             + " to a non-existing display:%d", displayId);
-                    return false;
+                    return null;
                 }
                 // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
                 // the feature b/155340867 is completed.
                 final DisplayArea da = dc.findAreaForWindowType(type, options,
                         callerCanManageAppTokens, false /* roundedCornerOverlay */);
+                // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
+                //  in return value of attachWindowContextToDisplayArea.
                 mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
                         callingUid, type, options);
-                return true;
+                return da.getConfiguration();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5390,6 +5395,25 @@
         }
     }
 
+    void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
+        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                if (displayContent != null) {
+                    displayContent.setSandboxDisplayApis(sandboxDisplayApis);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     /** The global settings only apply to default display. */
     private boolean applyForcedPropertiesForDefaultDisplay() {
         boolean changed = false;
@@ -7431,20 +7455,18 @@
         }
 
         @Override
-        public boolean setWindowsForAccessibilityCallback(int displayId,
+        public void setWindowsForAccessibilityCallback(int displayId,
                 WindowsForAccessibilityCallback callback) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController == null) {
                     mAccessibilityController = new AccessibilityController(
                             WindowManagerService.this);
                 }
-                final boolean result =
-                        mAccessibilityController.setWindowsForAccessibilityCallback(
+                mAccessibilityController.setWindowsForAccessibilityCallback(
                         displayId, callback);
                 if (!mAccessibilityController.hasCallbacks()) {
                     mAccessibilityController = null;
                 }
-                return result;
             }
         }
 
@@ -7571,6 +7593,7 @@
         public void registerAppTransitionListener(AppTransitionListener listener) {
             synchronized (mGlobalLock) {
                 getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
+                mAtmService.getTransitionController().registerLegacyListener(listener);
             }
         }
 
@@ -7840,7 +7863,10 @@
         @Override
         public boolean isTouchOrFaketouchDevice() {
             synchronized (mGlobalLock) {
-                // All touchable devices are also faketouchable.
+                if (mIsTouchDevice && !mIsFakeTouchDevice) {
+                    throw new IllegalStateException(
+                            "touchscreen supported device must report faketouch.");
+                }
                 return mIsFakeTouchDevice;
             }
         }
@@ -8094,11 +8120,21 @@
             // This could prevent if there is no container animation, we still have to apply the
             // pending transaction and exit waiting.
             mAnimator.mNotifyWhenNoAnimation = true;
+            boolean animateStarting = false;
             while (timeoutRemaining > 0) {
+                // Waiting until all starting windows has finished animating.
+                animateStarting = mRoot.forAllActivities(a -> {
+                    return a.hasStartingWindow();
+                });
                 boolean isAnimating = mAnimator.isAnimationScheduled()
-                        || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL);
+                        || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
+                        || animateStarting;
                 if (!isAnimating) {
-                    break;
+                    // isAnimating is a legacy transition query and will be removed, so also add
+                    // a check for whether this is in a shell-transition when not using legacy.
+                    if (!mAtmService.getTransitionController().inTransition()) {
+                        break;
+                    }
                 }
                 long startTime = System.currentTimeMillis();
                 try {
@@ -8112,13 +8148,14 @@
             WindowContainer animatingContainer;
             animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
                     ANIMATION_TYPE_ALL);
-            if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
+            if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) {
                 Slog.w(TAG, "Timed out waiting for animations to complete,"
                         + " animatingContainer=" + animatingContainer
                         + " animationType=" + SurfaceAnimator.animationTypeToString(
                         animatingContainer != null
                                 ? animatingContainer.mSurfaceAnimator.getAnimationType()
-                                : SurfaceAnimator.ANIMATION_TYPE_NONE));
+                                : SurfaceAnimator.ANIMATION_TYPE_NONE)
+                        + " animateStarting=" + animateStarting);
             }
         }
     }
@@ -8160,11 +8197,11 @@
             displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
                     true /* includingParents */);
         }
-        handleTaskFocusChange(touchedWindow.getTask());
+        handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
     }
 
     @VisibleForTesting
-    void handleTaskFocusChange(Task task) {
+    void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) {
         if (task == null) {
             return;
         }
@@ -8183,7 +8220,7 @@
             }
         }
 
-        mAtmService.setFocusedTask(task.mTaskId);
+        mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
     }
 
     /**
@@ -8556,7 +8593,7 @@
             }
 
             if (win.mActivityRecord == null || !win.mActivityRecord.isState(
-                    Task.ActivityState.RESUMED)) {
+                    ActivityRecord.State.RESUMED)) {
                 mDisplayHashController.sendDisplayHashError(callback,
                         DISPLAY_HASH_ERROR_MISSING_WINDOW);
                 return;
@@ -8611,4 +8648,23 @@
             return snapshot != null && snapshot.hasImeSurface();
         }
     }
+
+    @Override
+    public int getImeDisplayId() {
+        // TODO(b/189805422): Add a toast to notify users that IMS may get extra
+        //  onConfigurationChanged callback when perDisplayFocus is enabled.
+        //  Enabling perDisplayFocus means that we track focus on each display, so we don't have
+        //  the "top focus" display and getTopFocusedDisplayContent returns the default display
+        //  as the fallback. It leads to InputMethodService receives an extra onConfiguration
+        //  callback when InputMethodService move from a secondary display to another display
+        //  with the same display metrics because InputMethodService will always associate with
+        //  the ImeContainer on the default display in onCreate and receive a configuration update
+        //  to match default display ImeContainer and then receive another configuration update
+        //  from attachToWindowToken.
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getTopFocusedDisplayContent();
+            return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId()
+                    : DEFAULT_DISPLAY;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a94fd07..d5965494 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,6 +19,12 @@
 import static android.os.Build.IS_USER;
 import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
 
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.ParcelFileDescriptor;
@@ -36,6 +42,7 @@
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -58,10 +65,12 @@
 
     // Internal service impl -- must perform security checks before touching.
     private final WindowManagerService mInternal;
+    private final LetterboxConfiguration mLetterboxConfiguration;
 
     public WindowManagerShellCommand(WindowManagerService service) {
         mInterface = service;
         mInternal = service;
+        mLetterboxConfiguration = service.mLetterboxConfiguration;
     }
 
     @Override
@@ -113,6 +122,14 @@
                     return runGetIgnoreOrientationRequest(pw);
                 case "dump-visible-window-views":
                     return runDumpVisibleWindowViews(pw);
+                case "set-letterbox-style":
+                    return runSetLetterboxStyle(pw);
+                case "get-letterbox-style":
+                    return runGetLetterboxStyle(pw);
+                case "reset-letterbox-style":
+                    return runResetLetterboxStyle(pw);
+                case "set-sandbox-display-apis":
+                    return runSandboxDisplayApis(pw);
                 case "set-multi-window-config":
                     return runSetMultiWindowConfig();
                 case "get-multi-window-config":
@@ -331,6 +348,37 @@
         return 0;
     }
 
+    /**
+     * Override display size and metrics to reflect the DisplayArea of the calling activity.
+     */
+    private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
+        int displayId = Display.DEFAULT_DISPLAY;
+        String arg = getNextArgRequired();
+        if ("-d".equals(arg)) {
+            displayId = Integer.parseInt(getNextArgRequired());
+            arg = getNextArgRequired();
+        }
+
+        final boolean sandboxDisplayApis;
+        switch (arg) {
+            case "true":
+            case "1":
+                sandboxDisplayApis = true;
+                break;
+            case "false":
+            case "0":
+                sandboxDisplayApis = false;
+                break;
+            default:
+                getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+                        + "get " + arg);
+                return -1;
+        }
+
+        mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
+        return 0;
+    }
+
     private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
         mInterface.dismissKeyguard(null /* callback */, null /* message */);
         return 0;
@@ -548,6 +596,231 @@
         return 0;
     }
 
+    private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+        final float aspectRatio;
+        try {
+            String arg = getNextArgRequired();
+            aspectRatio = Float.parseFloat(arg);
+        } catch (NumberFormatException  e) {
+            getErrPrintWriter().println("Error: bad aspect ratio format " + e);
+            return -1;
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or aspect ratio should be provided as an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+        final int cornersRadius;
+        try {
+            String arg = getNextArgRequired();
+            cornersRadius = Integer.parseInt(arg);
+        } catch (NumberFormatException  e) {
+            getErrPrintWriter().println("Error: bad corners radius format " + e);
+            return -1;
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or corners radius should be provided as an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+        @LetterboxBackgroundType final int backgroundType;
+        try {
+            String arg = getNextArgRequired();
+            switch (arg) {
+                case "solid_color":
+                    backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+                    break;
+                case "app_color_background":
+                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+                    break;
+                case "app_color_background_floating":
+                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+                    break;
+                case "wallpaper":
+                    backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
+                    break;
+                default:
+                    getErrPrintWriter().println(
+                            "Error: 'reset', 'solid_color', 'app_color_background' or "
+                            + "'wallpaper' should be provided as an argument");
+                    return -1;
+            }
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset', 'solid_color', 'app_color_background' or "
+                        + "'wallpaper' should be provided as an argument" + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+        final Color color;
+        try {
+            String arg = getNextArgRequired();
+            color = Color.valueOf(Color.parseColor(arg));
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or color in #RRGGBB format should be provided as "
+                            + "an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundColor(color);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
+            throws RemoteException {
+        final int radius;
+        try {
+            String arg = getNextArgRequired();
+            radius = Integer.parseInt(arg);
+        } catch (NumberFormatException  e) {
+            getErrPrintWriter().println("Error: blur radius format " + e);
+            return -1;
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or blur radius should be provided as an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
+            throws RemoteException {
+        final float alpha;
+        try {
+            String arg = getNextArgRequired();
+            alpha = Float.parseFloat(arg);
+        } catch (NumberFormatException  e) {
+            getErrPrintWriter().println("Error: bad alpha format " + e);
+            return -1;
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or alpha should be provided as an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
+        }
+        return 0;
+    }
+
+    private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+        final float multiplier;
+        try {
+            String arg = getNextArgRequired();
+            multiplier = Float.parseFloat(arg);
+        } catch (NumberFormatException  e) {
+            getErrPrintWriter().println("Error: bad multiplier format " + e);
+            return -1;
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset' or multiplier should be provided as an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+        }
+        return 0;
+    }
+
+    private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        if (peekNextArg() == null) {
+            getErrPrintWriter().println("Error: No arguments provided.");
+        }
+        while (peekNextArg() != null) {
+            String arg = getNextArg();
+            switch (arg) {
+                case "--aspectRatio":
+                    runSetFixedOrientationLetterboxAspectRatio(pw);
+                    break;
+                case "--cornerRadius":
+                    runSetLetterboxActivityCornersRadius(pw);
+                    break;
+                case "--backgroundType":
+                    runSetLetterboxBackgroundType(pw);
+                    break;
+                case "--backgroundColor":
+                    runSetLetterboxBackgroundColor(pw);
+                    break;
+                case "--wallpaperBlurRadius":
+                    runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+                    break;
+                case "--wallpaperDarkScrimAlpha":
+                    runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+                    break;
+                case "--horizontalPositionMultiplier":
+                    runSeLetterboxHorizontalPositionMultiplier(pw);
+                    break;
+                default:
+                    getErrPrintWriter().println(
+                            "Error: Unrecognized letterbox style option: " + arg);
+                    return -1;
+            }
+        }
+        return 0;
+    }
+
+    private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        if (peekNextArg() == null) {
+            resetLetterboxStyle();
+        }
+        synchronized (mInternal.mGlobalLock) {
+            while (peekNextArg() != null) {
+                String arg = getNextArg();
+                switch (arg) {
+                    case "aspectRatio":
+                        mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+                        break;
+                    case "cornerRadius":
+                        mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+                        break;
+                    case "backgroundType":
+                        mLetterboxConfiguration.resetLetterboxBackgroundType();
+                        break;
+                    case "backgroundColor":
+                        mLetterboxConfiguration.resetLetterboxBackgroundColor();
+                        break;
+                    case "wallpaperBlurRadius":
+                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+                        break;
+                    case "wallpaperDarkScrimAlpha":
+                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+                        break;
+                    case "horizontalPositionMultiplier":
+                        mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+                        break;
+                    default:
+                        getErrPrintWriter().println(
+                                "Error: Unrecognized letterbox style option: " + arg);
+                        return -1;
+                }
+            }
+        }
+        return 0;
+    }
+
     private int runSetMultiWindowConfig() {
         if (peekNextArg() == null) {
             getErrPrintWriter().println("Error: No arguments provided.");
@@ -622,6 +895,40 @@
         return 0;
     }
 
+    private void resetLetterboxStyle() {
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+            mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+            mLetterboxConfiguration.resetLetterboxBackgroundType();
+            mLetterboxConfiguration.resetLetterboxBackgroundColor();
+            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+            mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+        }
+    }
+
+    private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        synchronized (mInternal.mGlobalLock) {
+            pw.println("Corner radius: "
+                    + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+            pw.println("Horizontal position multiplier: "
+                    + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+            pw.println("Aspect ratio: "
+                    + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+
+            pw.println("Background type: "
+                    + LetterboxConfiguration.letterboxBackgroundTypeToString(
+                            mLetterboxConfiguration.getLetterboxBackgroundType()));
+            pw.println("    Background color: " + Integer.toHexString(
+                    mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+            pw.println("    Wallpaper blur radius: "
+                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+            pw.println("    Wallpaper dark scrim alpha: "
+                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+        }
+        return 0;
+    }
+
     private int runReset(PrintWriter pw) throws RemoteException {
         int displayId = getDisplayId(getNextArg());
 
@@ -646,6 +953,12 @@
         // set-ignore-orientation-request
         mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
 
+        // set-letterbox-style
+        resetLetterboxStyle();
+
+        // set-sandbox-display-apis
+        mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
+
         // set-multi-window-config
         runResetMultiWindowConfig();
 
@@ -680,7 +993,12 @@
         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("  set-sandbox-display-apis [true|1|false|0]");
+        pw.println("    Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
+        pw.println("    DisplayArea of the activity, or the window bounds if in letterbox or");
+        pw.println("    Size Compat Mode.");
 
+        printLetterboxHelp(pw);
         printMultiWindowConfigHelp(pw);
 
         pw.println("  reset [-d DISPLAY_ID]");
@@ -693,6 +1011,49 @@
         }
     }
 
+    private void printLetterboxHelp(PrintWriter pw) {
+        pw.println("  set-letterbox-style");
+        pw.println("    Sets letterbox style using the following options:");
+        pw.println("      --aspectRatio aspectRatio");
+        pw.println("        Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+                + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+        pw.println("        both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+        pw.println("        be ignored and framework implementation will determine aspect ratio.");
+        pw.println("      --cornerRadius radius");
+        pw.println("        Corners radius for activities in the letterbox mode. If radius < 0,");
+        pw.println("        both it and R.integer.config_letterboxActivityCornersRadius will be");
+        pw.println("        ignored and corners of the activity won't be rounded.");
+        pw.println("      --backgroundType [reset|solid_color|app_color_background");
+        pw.println("          |app_color_background_floating|wallpaper]");
+        pw.println("        Type of background used in the letterbox mode.");
+        pw.println("      --backgroundColor color");
+        pw.println("        Color of letterbox which is be used when letterbox background type");
+        pw.println("        is 'solid-color'. Use (set)get-letterbox-style to check and control");
+        pw.println("        letterbox background type. See Color#parseColor for allowed color");
+        pw.println("        formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+        pw.println("      --wallpaperBlurRadius radius");
+        pw.println("        Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+        pw.println("        both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+        pw.println("        are ignored and 0 is used.");
+        pw.println("      --wallpaperDarkScrimAlpha alpha");
+        pw.println("        Alpha of a black translucent scrim shown over 'wallpaper'");
+        pw.println("        letterbox background. If alpha < 0 or >= 1 both it and");
+        pw.println("        R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+        pw.println("        and 0.0 (transparent) is used instead.");
+        pw.println("      --horizontalPositionMultiplier multiplier");
+        pw.println("        Horizontal position of app window center. If multiplier < 0 or > 1,");
+        pw.println("        both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+        pw.println("        are ignored and central position (0.5) is used.");
+        pw.println("  reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+        pw.println("      |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+        pw.println("      |horizontalPositionMultiplier]");
+        pw.println("    Resets overrides to default values for specified properties separated");
+        pw.println("    by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+        pw.println("    If no arguments provided, all values will be reset.");
+        pw.println("  get-letterbox-style");
+        pw.println("    Prints letterbox style configuration.");
+    }
+
     private void printMultiWindowConfigHelp(PrintWriter pw) {
         pw.println("  set-multi-window-config");
         pw.println("    Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a82a478..e31b130 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -16,13 +16,22 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.isStartResultSuccessful;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -33,7 +42,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -42,14 +55,19 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizer;
+import android.window.ITaskFragmentOrganizerController;
 import android.window.ITaskOrganizerController;
 import android.window.ITransitionPlayer;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.IWindowOrganizerController;
+import android.window.TaskFragmentCreationParams;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -95,14 +113,21 @@
 
     final TaskOrganizerController mTaskOrganizerController;
     final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
+    final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
 
     final TransitionController mTransitionController;
+    /**
+     * A Map which manages the relationship between
+     * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
+     */
+    private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
 
     WindowOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
         mGlobalLock = atm.mGlobalLock;
         mTaskOrganizerController = new TaskOrganizerController(mService);
         mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
+        mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
         mTransitionController = new TransitionController(atm);
     }
 
@@ -122,14 +147,15 @@
 
     @Override
     public void applyTransaction(WindowContainerTransaction t) {
-        enforceTaskPermission("applyTransaction()");
         if (t == null) {
-            throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
+            throw new IllegalArgumentException("Null transaction passed to applyTransaction");
         }
+        enforceTaskPermission("applyTransaction()", t);
+        final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                applyTransaction(t, -1 /*syncId*/, null /*transition*/);
+                applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -139,10 +165,11 @@
     @Override
     public int applySyncTransaction(WindowContainerTransaction t,
             IWindowContainerTransactionCallback callback) {
-        enforceTaskPermission("applySyncTransaction()");
         if (t == null) {
             throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
         }
+        enforceTaskPermission("applySyncTransaction()", t);
+        final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -162,7 +189,7 @@
                 if (callback != null) {
                     syncId = startSyncWithOrganizer(callback);
                 }
-                applyTransaction(t, syncId, null /*transition*/);
+                applyTransaction(t, syncId, null /*transition*/, caller);
                 if (syncId >= 0) {
                     setSyncReady(syncId);
                 }
@@ -177,6 +204,7 @@
     public IBinder startTransition(int type, @Nullable IBinder transitionToken,
             @Nullable WindowContainerTransaction t) {
         enforceTaskPermission("startTransition()");
+        final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -196,7 +224,7 @@
                             throw new IllegalArgumentException("Can't use legacy transitions in"
                                     + " compatibility mode with no WCT.");
                         }
-                        applyTransaction(t, -1 /* syncId */, null);
+                        applyTransaction(t, -1 /* syncId */, null, caller);
                         return null;
                     }
                     transition = mTransitionController.createTransition(type);
@@ -205,9 +233,9 @@
                 if (t == null) {
                     t = new WindowContainerTransaction();
                 }
-                applyTransaction(t, -1 /*syncId*/, transition);
+                applyTransaction(t, -1 /*syncId*/, transition, caller);
                 if (needsSetReady) {
-                    transition.setReady();
+                    transition.setAllReady();
                 }
                 return transition;
             }
@@ -217,10 +245,49 @@
     }
 
     @Override
+    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
+            @NonNull IWindowContainerTransactionCallback callback,
+            @NonNull WindowContainerTransaction t) {
+        enforceTaskPermission("startLegacyTransition()");
+        final CallerInfo caller = new CallerInfo();
+        final long ident = Binder.clearCallingIdentity();
+        int syncId;
+        try {
+            synchronized (mGlobalLock) {
+                if (type < 0) {
+                    throw new IllegalArgumentException("Can't create transition with no type");
+                }
+                if (mTransitionController.getTransitionPlayer() != null) {
+                    throw new IllegalArgumentException("Can't use legacy transitions in"
+                            + " when shell transitions are enabled.");
+                }
+                final DisplayContent dc =
+                        mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+                if (dc.mAppTransition.isTransitionSet()) {
+                    // a transition already exists, so the callback probably won't be called.
+                    return -1;
+                }
+                adapter.setCallingPidUid(caller.mPid, caller.mUid);
+                dc.prepareAppTransition(type);
+                dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
+                syncId = startSyncWithOrganizer(callback);
+                applyTransaction(t, syncId, null /* transition */, caller);
+                setSyncReady(syncId);
+                mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY)
+                        .executeAppTransition();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return syncId;
+    }
+
+    @Override
     public int finishTransition(@NonNull IBinder transitionToken,
             @Nullable WindowContainerTransaction t,
             @Nullable IWindowContainerTransactionCallback callback) {
         enforceTaskPermission("finishTransition()");
+        final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -231,7 +298,7 @@
                 // apply the incoming transaction before finish in case it alters the visibility
                 // of the participants.
                 if (t != null) {
-                    applyTransaction(t, syncId, null /*transition*/);
+                    applyTransaction(t, syncId, null /*transition*/, caller);
                 }
                 getTransitionController().finishTransition(transitionToken);
                 if (syncId >= 0) {
@@ -247,9 +314,10 @@
     /**
      * @param syncId If non-null, this will be a sync-transaction.
      * @param transition A transition to collect changes into.
+     * @param caller Info about the calling process.
      */
     private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @Nullable Transition transition) {
+            @Nullable Transition transition, @Nullable CallerInfo caller) {
         int effects = 0;
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
         mService.deferWindowLayout();
@@ -303,7 +371,8 @@
                 final boolean isInLockTaskMode = mService.isInLockTaskMode();
                 for (int i = 0; i < hopSize; ++i) {
                     effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
-                            isInLockTaskMode);
+                            isInLockTaskMode, caller, t.getErrorCallbackToken(),
+                            t.getTaskFragmentOrganizer());
                 }
             }
             // Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -458,7 +527,9 @@
     }
 
     private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
-            int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
+            int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
+            @Nullable CallerInfo caller, @Nullable IBinder errorCallbackToken,
+            @Nullable ITaskFragmentOrganizer organizer) {
         final int type = hop.getType();
         switch (type) {
             case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
@@ -480,7 +551,7 @@
                 } else if (!task.mCreatedByOrganizer) {
                     throw new UnsupportedOperationException(
                             "Cannot set non-organized task as adjacent flag root: " + wc);
-                } else if (task.mAdjacentTask == null) {
+                } else if (task.getAdjacentTaskFragment() == null) {
                     throw new UnsupportedOperationException(
                             "Cannot set non-adjacent task as adjacent flag root: " + wc);
                 }
@@ -501,13 +572,15 @@
             return effects;
         }
 
+        final WindowContainer wc;
+        final IBinder fragmentToken;
         switch (type) {
             case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
                 effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
                 break;
             case HIERARCHY_OP_TYPE_REORDER:
             case HIERARCHY_OP_TYPE_REPARENT:
-                final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+                wc = WindowContainer.fromBinder(hop.getContainer());
                 if (wc == null || !wc.isAttached()) {
                     Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                     break;
@@ -537,11 +610,109 @@
                 effects |= sanitizeAndApplyHierarchyOp(wc, hop);
                 break;
             case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+                mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+                        "launchTask HierarchyOp");
                 final Bundle launchOpts = hop.getLaunchOptions();
                 final int taskId = launchOpts.getInt(
                         WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
                 launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
-                mService.startActivityFromRecents(taskId, launchOpts);
+                final SafeActivityOptions safeOptions = caller != null
+                        ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
+                        : SafeActivityOptions.fromBundle(launchOpts);
+                mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
+                        taskId, safeOptions);
+                break;
+            case HIERARCHY_OP_TYPE_PENDING_INTENT:
+                String resolvedType = hop.getActivityIntent() != null
+                        ? hop.getActivityIntent().resolveTypeIfNeeded(
+                                mService.mContext.getContentResolver())
+                        : null;
+
+                Bundle options = null;
+                if (hop.getPendingIntent().isActivity()) {
+                    // Set the context display id as preferred for this activity launches, so that
+                    // it can land on caller's display. Or just brought the task to front at the
+                    // display where it was on since it has higher preference.
+                    ActivityOptions activityOptions = hop.getLaunchOptions() != null
+                            ? new ActivityOptions(hop.getLaunchOptions())
+                            : ActivityOptions.makeBasic();
+                    activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
+                    options = activityOptions.toBundle();
+                }
+
+                int res = mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
+                        hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
+                        hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
+                        null /* requiredPermission */, options);
+                if (res != ActivityManager.START_SUCCESS
+                        && res != ActivityManager.START_TASK_TO_FRONT) {
+                    if (!mTransitionController.isShellTransitionsEnabled()) {
+                        final DisplayContent dc =
+                                mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+                        dc.cancelAppTransition();
+                    }
+                }
+                break;
+            case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+                final TaskFragmentCreationParams taskFragmentCreationOptions =
+                        hop.getTaskFragmentCreationOptions();
+                createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+                break;
+            case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+                wc = WindowContainer.fromBinder(hop.getContainer());
+                if (wc == null || !wc.isAttached()) {
+                    Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+                    break;
+                }
+                final TaskFragment taskFragment = wc.asTaskFragment();
+                if (taskFragment == null || taskFragment.asTask() != null) {
+                    throw new IllegalArgumentException(
+                            "Can only delete organized TaskFragment, but not Task.");
+                }
+                deleteTaskFragment(taskFragment, errorCallbackToken);
+                break;
+            case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+                fragmentToken = hop.getContainer();
+                if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+                    final Throwable exception = new IllegalArgumentException(
+                            "Not allowed to operate with invalid fragment token");
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    break;
+                }
+                final Intent activityIntent = hop.getActivityIntent();
+                final Bundle activityOptions = hop.getLaunchOptions();
+                final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
+                final int result = mService.getActivityStartController()
+                        .startActivityInTaskFragment(tf, activityIntent, activityOptions);
+                if (!isStartResultSuccessful(result)) {
+                    final Throwable exception =
+                            new ActivityNotFoundException("start activity in taskFragment failed");
+                    sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
+                            errorCallbackToken, exception);
+                }
+                break;
+            case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+                fragmentToken = hop.getNewParent();
+                final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+                if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+                    final Throwable exception = new IllegalArgumentException(
+                            "Not allowed to operate with invalid fragment token or activity.");
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    break;
+                }
+                activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+                break;
+            case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+                final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
+                final WindowContainer newParent = hop.getNewParent() != null
+                        ? WindowContainer.fromBinder(hop.getNewParent())
+                        : null;
+                if (oldParent == null || !oldParent.isAttached()) {
+                    Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+                            + oldParent);
+                    break;
+                }
+                reparentTaskFragment(oldParent, newParent, errorCallbackToken);
                 break;
         }
         return effects;
@@ -704,19 +875,20 @@
     }
 
     private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
-        final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask();
-        final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask();
+        final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
+        final TaskFragment root2 =
+                WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment();
         if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
             throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
                     + " organizer root1=" + root1 + " root2=" + root2);
         }
-        root1.setAdjacentTask(root2);
+        root1.setAdjacentTaskFragment(root2);
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
 
     private void sanitizeWindowContainer(WindowContainer wc) {
-        if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
-            throw new RuntimeException("Invalid token in task or displayArea transaction");
+        if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) {
+            throw new RuntimeException("Invalid token in task fragment or displayArea transaction");
         }
     }
 
@@ -747,6 +919,11 @@
         return mDisplayAreaOrganizerController;
     }
 
+    @Override
+    public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() {
+        return mTaskFragmentOrganizerController;
+    }
+
     @VisibleForTesting
     int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
         int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
@@ -795,7 +972,204 @@
         }
     }
 
+    /** Whether the configuration changes are important to report back to an organizer. */
+    static boolean configurationsAreEqualForOrganizer(
+            Configuration newConfig, @Nullable Configuration oldConfig) {
+        if (oldConfig == null) {
+            return false;
+        }
+        int cfgChanges = newConfig.diff(oldConfig);
+        final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+                ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration,
+                true /* compareUndefined */) : 0;
+        if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) {
+            cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+        }
+        return (cfgChanges & CONTROLLABLE_CONFIGS) == 0;
+    }
+
     private void enforceTaskPermission(String func) {
         mService.enforceTaskPermission(func);
     }
+
+    private void enforceTaskPermission(String func, WindowContainerTransaction t) {
+        if (t == null || t.getTaskFragmentOrganizer() == null) {
+            enforceTaskPermission(func);
+            return;
+        }
+
+        // Apps may not have the permission to manage Tasks, but we are allowing apps to manage
+        // TaskFragments belonging to their own Task.
+        enforceOperationsAllowedForTaskFragmentOrganizer(func, t);
+    }
+
+    /**
+     * Makes sure that the transaction only contains operations that are allowed for the
+     * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
+     */
+    private void enforceOperationsAllowedForTaskFragmentOrganizer(
+            String func, WindowContainerTransaction t) {
+        final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
+
+        // Configuration changes
+        final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+                t.getChanges().entrySet().iterator();
+        while (entries.hasNext()) {
+            final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
+            // Only allow to apply changes to TaskFragment that is created by this organizer.
+            enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
+                    organizer);
+        }
+
+        // Hierarchy changes
+        final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+        for (int i = hops.size() - 1; i >= 0; i--) {
+            final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+            final int type = hop.getType();
+            // Check for each type of the operations that are allowed for TaskFragmentOrganizer.
+            switch (type) {
+                case HIERARCHY_OP_TYPE_REORDER:
+                case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+                    enforceTaskFragmentOrganized(func,
+                            WindowContainer.fromBinder(hop.getContainer()), organizer);
+                    break;
+                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+                    enforceTaskFragmentOrganized(func,
+                            WindowContainer.fromBinder(hop.getContainer()), organizer);
+                    enforceTaskFragmentOrganized(func,
+                            WindowContainer.fromBinder(hop.getAdjacentRoot()),
+                            organizer);
+                    break;
+                case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+                    // We are allowing organizer to create TaskFragment. We will check the
+                    // ownerToken in #createTaskFragment, and trigger error callback if that is not
+                    // valid.
+                case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+                case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+                    // We are allowing organizer to start/reparent activity to a TaskFragment it
+                    // created. Nothing to check here because the TaskFragment may not be created
+                    // yet, but will be created in the same transaction.
+                    break;
+                case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+                    enforceTaskFragmentOrganized(func,
+                            WindowContainer.fromBinder(hop.getContainer()), organizer);
+                    if (hop.getNewParent() != null) {
+                        enforceTaskFragmentOrganized(func,
+                                WindowContainer.fromBinder(hop.getNewParent()),
+                                organizer);
+                    }
+                    break;
+                default:
+                    // Other types of hierarchy changes are not allowed.
+                    String msg = "Permission Denial: " + func + " from pid="
+                            + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                            + " trying to apply a hierarchy change that is not allowed for"
+                            + " TaskFragmentOrganizer=" + organizer;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+            }
+        }
+    }
+
+    private void enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc,
+            ITaskFragmentOrganizer organizer) {
+        if (wc == null) {
+            Slog.e(TAG, "Attempt to operate on window that no longer exists");
+            return;
+        }
+
+        final TaskFragment tf = wc.asTaskFragment();
+        if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
+            String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid() + " trying to modify window container not"
+                    + " belonging to the TaskFragmentOrganizer=" + organizer;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+    }
+
+    void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
+            @Nullable IBinder errorCallbackToken) {
+        final ActivityRecord ownerActivity =
+                ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
+        if (ownerActivity == null || ownerActivity.getTask() == null) {
+            final Throwable exception =
+                    new IllegalArgumentException("Not allowed to operate with invalid ownerToken");
+            sendTaskFragmentOperationFailure(creationParams.getOrganizer(), errorCallbackToken,
+                    exception);
+            return;
+        }
+        // The ownerActivity has to belong to the same app as the root Activity of the target Task.
+        final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
+        if (rootActivity.getUid() != ownerActivity.getUid()) {
+            final Throwable exception =
+                    new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+                            + "the root activity of the target task belong to the different app");
+            sendTaskFragmentOperationFailure(creationParams.getOrganizer(), errorCallbackToken,
+                    exception);
+            return;
+        }
+        final TaskFragment taskFragment = new TaskFragment(mService,
+                creationParams.getFragmentToken(), true /* createdByOrganizer */);
+        // Set task fragment organizer immediately, since it might have to be notified about further
+        // actions.
+        taskFragment.setTaskFragmentOrganizer(
+                creationParams.getOrganizer(), ownerActivity.getPid());
+        ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
+        taskFragment.setWindowingMode(creationParams.getWindowingMode());
+        taskFragment.setBounds(creationParams.getInitialBounds());
+        mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
+    }
+
+    void reparentTaskFragment(@NonNull WindowContainer oldParent,
+            @Nullable WindowContainer newParent,  @Nullable IBinder errorCallbackToken) {
+        WindowContainer parent = newParent;
+        if (parent == null && oldParent.asTaskFragment() != null) {
+            parent = oldParent.asTaskFragment().getTask();
+        }
+        if (parent == null) {
+            final Throwable exception =
+                    new IllegalArgumentException("Not allowed to operate with invalid container");
+            sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(),
+                    errorCallbackToken, exception);
+            return;
+        }
+        while (oldParent.hasChild()) {
+            oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
+        }
+    }
+
+    void deleteTaskFragment(@NonNull TaskFragment taskFragment,
+            @Nullable IBinder errorCallbackToken) {
+        final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
+        if (index < 0) {
+            final Throwable exception =
+                    new IllegalArgumentException("Not allowed to operate with invalid "
+                            + "taskFragment");
+            sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
+                    errorCallbackToken, exception);
+            return;
+        }
+        mLaunchTaskFragments.removeAt(index);
+        taskFragment.removeImmediately();
+    }
+
+    static class CallerInfo {
+        final int mPid;
+        final int mUid;
+
+        CallerInfo() {
+            mPid = Binder.getCallingPid();
+            mUid = Binder.getCallingUid();
+        }
+    }
+
+    void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer,
+            @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) {
+        if (organizer == null) {
+            throw new IllegalArgumentException("Not allowed to operate with invalid organizer");
+        }
+        mService.mTaskFragmentOrganizerController
+                .onTaskFragmentError(organizer, errorCallbackToken, exception);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1364c72..65b065a 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -25,6 +25,13 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -32,13 +39,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-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;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -75,6 +75,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -219,6 +220,10 @@
     /** Whether our process is currently running a {@link IRemoteAnimationRunner} */
     private boolean mRunningRemoteAnimation;
 
+    /** List of "chained" processes that are running remote animations for this process */
+    private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
+            new ArrayList<>();
+
     // The bits used for mActivityStateFlags.
     private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
     private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -735,10 +740,10 @@
         if (canUpdate) {
             // Make sure the previous top activity in the process no longer be resumed.
             if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
-                final Task task = mPreQTopResumedActivity.getTask();
-                if (task != null) {
-                    boolean userLeaving = task.shouldBeVisible(null);
-                    task.startPausingLocked(userLeaving, false /* uiSleeping */,
+                final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
+                if (taskFrag != null) {
+                    boolean userLeaving = taskFrag.shouldBeVisible(null);
+                    taskFrag.startPausing(userLeaving, false /* uiSleeping */,
                             activity, "top-resumed-changed");
                 }
             }
@@ -991,7 +996,7 @@
         // Since there could be more than one activities in a process record, we don't need to
         // compute the OomAdj with each of them, just need to find out the activity with the
         // "best" state, the order would be visible, pausing, stopping...
-        Task.ActivityState bestInvisibleState = DESTROYED;
+        ActivityRecord.State bestInvisibleState = DESTROYED;
         boolean allStoppingFinishing = true;
         boolean visible = false;
         int minTaskLayer = Integer.MAX_VALUE;
@@ -1215,12 +1220,12 @@
                 hasVisibleActivities = true;
             }
 
-            final Task task = r.getTask();
-            if (task != null) {
+            final TaskFragment taskFragment = r.getTaskFragment();
+            if (taskFragment != null) {
                 // There may be a pausing activity that hasn't shown any window and was requested
                 // to be hidden. But pausing is also a visible state, it should be regarded as
                 // visible, so the caller can know the next activity should be resumed.
-                hasVisibleActivities |= task.handleAppDied(this);
+                hasVisibleActivities |= taskFragment.handleAppDied(this);
             }
             r.handleAppDied();
         }
@@ -1596,11 +1601,38 @@
         updateRunningRemoteOrRecentsAnimation();
     }
 
+    /**
+     * Marks another process as a "delegate" animator. This means that process is doing some part
+     * of a remote animation on behalf of this process.
+     */
+    void addRemoteAnimationDelegate(WindowProcessController delegate) {
+        if (!isRunningRemoteTransition()) {
+            throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
+                    + " running a remote animation");
+        }
+        mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
+    }
+
     void updateRunningRemoteOrRecentsAnimation() {
+        if (!isRunningRemoteTransition()) {
+            // Clean-up any delegates
+            for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
+                final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
+                if (delegate == null) continue;
+                delegate.setRunningRemoteAnimation(false);
+                delegate.setRunningRecentsAnimation(false);
+            }
+            mRemoteAnimationDelegates.clear();
+        }
+
         // Posting on handler so WM lock isn't held when we call into AM.
         mAtm.mH.sendMessage(PooledLambda.obtainMessage(
                 WindowProcessListener::setRunningRemoteAnimation, mListener,
-                mRunningRecentsAnimation || mRunningRemoteAnimation));
+                isRunningRemoteTransition()));
+    }
+
+    boolean isRunningRemoteTransition() {
+        return mRunningRecentsAnimation || mRunningRemoteAnimation;
     }
 
     /** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3fc995..4b21fa5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -34,6 +34,7 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.SurfaceControl.getGlobalTransaction;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -192,6 +193,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
 import android.app.compat.CompatChanges;
@@ -202,6 +204,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Debug;
@@ -211,7 +214,6 @@
 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;
@@ -874,6 +876,15 @@
     }
 
     /**
+     * Returns all the requested visibilities.
+     *
+     * @return an {@link InsetsState} as the requested visibilities.
+     */
+    InsetsState getRequestedState() {
+        return mRequestedInsetsState;
+    }
+
+    /**
      * @see #getRequestedVisibility(int)
      */
     void updateRequestedVisibility(InsetsState state) {
@@ -1259,8 +1270,8 @@
         frame.inset(left, top, right, bottom);
     }
 
-    void computeFrameAndUpdateSourceFrame() {
-        computeFrame();
+    void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
+        computeFrame(displayFrames);
         // Update the source frame to provide insets to other windows during layout. If the
         // simulated frames exist, then this is not computing a stable result so just skip.
         if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {
@@ -1271,7 +1282,7 @@
     /**
      * Perform standard frame computation. The result can be obtained with getFrame() if so desired.
      */
-    void computeFrame() {
+    void computeFrame(DisplayFrames displayFrames) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1384,7 +1395,8 @@
         final int fw = windowFrames.mFrame.width();
         final int fh = windowFrames.mFrame.height();
 
-        applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
+        applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame,
+                displayFrames);
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
             if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
@@ -1432,16 +1444,6 @@
         }
     }
 
-    // TODO: Look into whether this override is still necessary.
-    @Override
-    public Rect getBounds() {
-        if (mActivityRecord != null) {
-            return mActivityRecord.getBounds();
-        } else {
-            return super.getBounds();
-        }
-    }
-
     /** Retrieves the current frame of the window that the application sees. */
     Rect getFrame() {
         return mWindowFrames.mFrame;
@@ -1477,6 +1479,18 @@
         return mAttrs;
     }
 
+    WindowManager.LayoutParams getLayoutingAttrs(int rotation) {
+        if (!INSETS_LAYOUT_GENERALIZATION) {
+            return mAttrs;
+        }
+        final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation;
+        if (paramsForRotation == null || paramsForRotation.length != 4
+                || paramsForRotation[rotation] == null) {
+            return mAttrs;
+        }
+        return paramsForRotation[rotation];
+    }
+
     /** Retrieves the flags used to disable system UI functions. */
     int getDisableFlags() {
         return mDisableFlags;
@@ -1715,6 +1729,10 @@
         return mActivityRecord != null ? mActivityRecord.getTask() : null;
     }
 
+    @Nullable TaskFragment getTaskFragment() {
+        return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null;
+    }
+
     @Nullable Task getRootTask() {
         final Task task = getTask();
         if (task != null) {
@@ -1842,9 +1860,8 @@
         return super.hasContentToDisplay();
     }
 
-    @Override
-    boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
+    private boolean isVisibleByPolicyOrInsets() {
+        return isVisibleByPolicy()
                 // If we don't have a provider, this window isn't used as a window generating
                 // insets, so nobody can hide it over the inset APIs.
                 && (mControllableInsetProvider == null
@@ -1852,11 +1869,18 @@
     }
 
     @Override
+    boolean isVisible() {
+        return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+    }
+
+    @Override
     boolean isVisibleRequested() {
-        if (shouldCheckTokenVisibleRequested()) {
-            return isVisible() && mToken.isVisibleRequested();
+        final boolean localVisibleRequested =
+                wouldBeVisibleRequestedIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+        if (localVisibleRequested && shouldCheckTokenVisibleRequested()) {
+            return mToken.isVisibleRequested();
         }
-        return isVisible();
+        return localVisibleRequested;
     }
 
     /**
@@ -1903,6 +1927,16 @@
         return !isWallpaper || mToken.isVisible();
     }
 
+    private boolean wouldBeVisibleRequestedIfPolicyIgnored() {
+        final WindowState parent = getParentWindow();
+        final boolean isParentHiddenRequested = parent != null && !parent.isVisibleRequested();
+        if (isParentHiddenRequested || mAnimatingExit || mDestroying) {
+            return false;
+        }
+        final boolean isWallpaper = mToken.asWallpaperToken() != null;
+        return !isWallpaper || mToken.isVisibleRequested();
+    }
+
     /**
      * Is this window visible, ignoring its app token? It is not visible if there is no surface,
      * or we are in the process of running an exit animation that will remove the surface.
@@ -2360,6 +2394,12 @@
 
     @Override
     void removeImmediately() {
+        if (!mRemoved) {
+            // Destroy surface before super call. The general pattern is that the children need
+            // to be removed before the parent (so that the sync-engine tracking works). Since
+            // WindowStateAnimator is a "virtual" child, we have to do it manually here.
+            mWinAnimator.destroySurfaceLocked(getSyncTransaction());
+        }
         super.removeImmediately();
 
         if (mRemoved) {
@@ -2401,8 +2441,6 @@
 
         disposeInputChannel();
 
-        mWinAnimator.destroySurfaceLocked(mTmpTransaction);
-        mTmpTransaction.apply();
         mSession.windowRemovedLocked();
         try {
             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
@@ -2878,9 +2916,14 @@
                 // means we need to intercept touches outside of that window. The dim layer
                 // user associated with the window (task or root task) will give us the good
                 // bounds, as they would be used to display the dim layer.
-                final Task task = getTask();
-                if (task != null) {
-                    task.getDimBounds(mTmpRect);
+                final TaskFragment taskFragment = getTaskFragment();
+                if (taskFragment != null) {
+                    final Task task = taskFragment.asTask();
+                    if (task != null) {
+                        task.getDimBounds(mTmpRect);
+                    } else {
+                        mTmpRect.set(taskFragment.getBounds());
+                    }
                 } else if (getRootTask() != null) {
                     getRootTask().getDimBounds(mTmpRect);
                 }
@@ -3863,7 +3906,7 @@
         final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged();
         final DisplayContent displayContent = getDisplayContent();
         final boolean alwaysConsumeSystemBars =
-                displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(this);
+                displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
         final int displayId = displayContent.getDisplayId();
 
         markRedrawForSyncReported();
@@ -4397,12 +4440,13 @@
     }
 
     private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
-            Rect displayFrame) {
+            Rect displayFrame, DisplayFrames displayFrames) {
         final int pw = containingFrame.width();
         final int ph = containingFrame.height();
         final Task task = getTask();
         final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
-        final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
+        final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation);
+        final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
 
         // We need to fit it to the display if either
         // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
@@ -4412,49 +4456,54 @@
         // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
         // the display.
         final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
-                || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
+                || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
         float x, y;
         int w,h;
 
         final boolean hasCompatScale = hasCompatScale();
-        if ((mAttrs.flags & FLAG_SCALED) != 0) {
-            if (mAttrs.width < 0) {
+        if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) {
+            // For the window with different layout attrs for different rotations, we need to avoid
+            // using requested size. Otherwise, when finishing a simulated rotation, the information
+            // coming from WindowManagerServices to the ViewRootImpl may not contain the correct
+            // value for the new rotation, and there will be a quick flash of wrong layout when the
+            // simulated activity faded out.
+            if (attrs.width < 0) {
                 w = pw;
             } else if (hasCompatScale) {
-                w = (int)(mAttrs.width * mGlobalScale + .5f);
+                w = (int) (attrs.width * mGlobalScale + .5f);
             } else {
-                w = mAttrs.width;
+                w = attrs.width;
             }
-            if (mAttrs.height < 0) {
+            if (attrs.height < 0) {
                 h = ph;
             } else if (hasCompatScale) {
-                h = (int)(mAttrs.height * mGlobalScale + .5f);
+                h = (int) (attrs.height * mGlobalScale + .5f);
             } else {
-                h = mAttrs.height;
+                h = attrs.height;
             }
         } else {
-            if (mAttrs.width == MATCH_PARENT) {
+            if (attrs.width == MATCH_PARENT) {
                 w = pw;
             } else if (hasCompatScale) {
-                w = (int)(mRequestedWidth * mGlobalScale + .5f);
+                w = (int) (mRequestedWidth * mGlobalScale + .5f);
             } else {
                 w = mRequestedWidth;
             }
-            if (mAttrs.height == MATCH_PARENT) {
+            if (attrs.height == MATCH_PARENT) {
                 h = ph;
             } else if (hasCompatScale) {
-                h = (int)(mRequestedHeight * mGlobalScale + .5f);
+                h = (int) (mRequestedHeight * mGlobalScale + .5f);
             } else {
                 h = mRequestedHeight;
             }
         }
 
         if (hasCompatScale) {
-            x = mAttrs.x * mGlobalScale;
-            y = mAttrs.y * mGlobalScale;
+            x = attrs.x * mGlobalScale;
+            y = attrs.y * mGlobalScale;
         } else {
-            x = mAttrs.x;
-            y = mAttrs.y;
+            x = attrs.x;
+            y = attrs.y;
         }
 
         if (inNonFullscreenContainer && !layoutInParentFrame()) {
@@ -4481,13 +4530,12 @@
         }
 
         // Set mFrame
-        Gravity.apply(mAttrs.gravity, w, h, containingFrame,
-                (int) (x + mAttrs.horizontalMargin * pw),
-                (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame);
-
+        Gravity.apply(attrs.gravity, w, h, containingFrame,
+                (int) (x + attrs.horizontalMargin * pw),
+                (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame);
         // Now make sure the window fits in the overall display frame.
         if (fitToDisplay) {
-            Gravity.applyDisplay(mAttrs.gravity, displayFrame, windowFrames.mFrame);
+            Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame);
         }
 
         // We need to make sure we update the CompatFrame as it is used for
@@ -4771,6 +4819,7 @@
         windowInfo.focused = isFocused();
         Task task = getTask();
         windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
+        windowInfo.taskId = task == null ? ActivityTaskManager.INVALID_TASK_ID : task.mTaskId;
         windowInfo.hasFlagWatchOutsideTouch =
                 (mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
 
@@ -5949,6 +5998,13 @@
             return mWinAnimator.finishDrawingLocked(postDrawTransaction);
         }
 
+        if (mActivityRecord != null
+                && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
+                && mAttrs.type == TYPE_APPLICATION_STARTING) {
+            mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
+                    .notifyStartingWindowDrawn(mActivityRecord);
+        }
+
         if (postDrawTransaction != null) {
             mSyncTransaction.merge(postDrawTransaction);
         }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fbfa400..fa32be3 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,9 +16,11 @@
 
 package com.android.server.wm;
 
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -229,6 +231,11 @@
             ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
                     "removeAllWindowsIfPossible: removing win=%s", win);
             win.removeIfPossible();
+            if (i > mChildren.size()) {
+                // It's possible for removeIfPossible to delete siblings (for example if it is a
+                // starting window, it will perform operations on the ActivityRecord).
+                i = mChildren.size();
+            }
         }
     }
 
@@ -451,9 +458,24 @@
     }
 
     Rect getFixedRotationBarContentFrame(int windowType) {
-        return isFixedRotationTransforming()
-                ? mFixedRotationTransformState.mBarContentFrames.get(windowType)
-                : null;
+        if (!isFixedRotationTransforming()) {
+            return null;
+        }
+        if (!INSETS_LAYOUT_GENERALIZATION) {
+            return mFixedRotationTransformState.mBarContentFrames.get(windowType);
+        }
+        final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames;
+        final Rect tmpRect = new Rect();
+        if (windowType == TYPE_NAVIGATION_BAR) {
+            tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR)
+                    .getFrame());
+        }
+        if (windowType == TYPE_STATUS_BAR) {
+            tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR)
+                    .getFrame());
+        }
+        tmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+        return tmpRect;
     }
 
     InsetsState getFixedRotationTransformInsetsState() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 53401fd..ab4f1f6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -30,10 +30,6 @@
         "BroadcastRadio/TunerCallback.cpp",
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
-        "gnss/GnssConfiguration.cpp",
-        "gnss/GnssMeasurement.cpp",
-        "gnss/GnssMeasurementCallback.cpp",
-        "gnss/Utils.cpp",
         "stats/SurfaceFlingerPuller.cpp",
         "com_android_server_adb_AdbDebuggingManager.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
@@ -122,6 +118,7 @@
         "libinputflinger",
         "libinputflinger_base",
         "libinputservice",
+        "libservices.core-gnss",
         "libstatshidl",
         "libstatspull",
         "libstatssocket",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a87b513..1639d82 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -40,6 +40,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "gnss/GnssAntennaInfoCallback.h"
 #include "gnss/GnssConfiguration.h"
 #include "gnss/GnssMeasurement.h"
 #include "gnss/Utils.h"
@@ -60,12 +61,7 @@
 
 static jclass class_location;
 static jclass class_gnssNavigationMessage;
-static jclass class_gnssAntennaInfoBuilder;
 static jclass class_gnssPowerStats;
-static jclass class_phaseCenterOffset;
-static jclass class_sphericalCorrections;
-static jclass class_arrayList;
-static jclass class_doubleArray;
 
 jobject android::mCallbacksObj = nullptr;
 
@@ -89,7 +85,6 @@
 static jmethodID method_reportGeofenceRemoveStatus;
 static jmethodID method_reportGeofencePauseStatus;
 static jmethodID method_reportGeofenceResumeStatus;
-static jmethodID method_reportAntennaInfo;
 static jmethodID method_reportNavigationMessages;
 static jmethodID method_reportLocationBatch;
 static jmethodID method_reportGnssServiceDied;
@@ -123,25 +118,9 @@
 static jmethodID method_isInEmergencySession;
 static jmethodID method_locationCtor;
 static jmethodID method_gnssNavigationMessageCtor;
-static jmethodID method_gnssAntennaInfoBuilderCtor;
 static jmethodID method_gnssPowerStatsCtor;
-static jmethodID method_phaseCenterOffsetCtor;
-static jmethodID method_sphericalCorrectionsCtor;
-static jmethodID method_arrayListCtor;
-static jmethodID method_arrayListAdd;
-static jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
-static jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
-static jmethodID method_gnssAntennaInfoBuilderBuild;
 static jmethodID method_setSubHalPowerIndicationCapabilities;
 
-/*
- * Save a pointer to JavaVm to attach/detach threads executing
- * callback methods that need to make JNI calls.
- */
-JavaVM* android::ScopedJniThreadAttach::sJvm;
-
 using android::OK;
 using android::sp;
 using android::status_t;
@@ -193,7 +172,6 @@
 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;
-using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
 using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
 using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
 using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
@@ -954,208 +932,6 @@
 }
 
 /*
- * GnssAntennaInfoCallback implements the callback methods required for the
- * GnssAntennaInfo interface.
- */
-struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
-    // Methods from V2_1::GnssAntennaInfoCallback follow.
-    Return<void> gnssAntennaInfoCb(
-            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
-
-private:
-    jobject translateAllGnssAntennaInfos(
-            JNIEnv* env,
-            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
-    jobject translateSingleGnssAntennaInfo(
-            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
-    jobject translatePhaseCenterOffset(
-            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
-    jobject translatePhaseCenterVariationCorrections(
-            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
-    jobject translateSignalGainCorrections(
-            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
-    jobjectArray translate2dDoubleArray(JNIEnv* env,
-                                        const hidl_vec<IGnssAntennaInfoCallback::Row>& array);
-    void translateAndReportGnssAntennaInfo(
-            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
-    void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
-};
-
-Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
-        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
-    translateAndReportGnssAntennaInfo(gnssAntennaInfos);
-    return Void();
-}
-
-jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
-        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
-    jsize numRows = array.size();
-    if (numRows == 0) {
-        // Empty array
-        return NULL;
-    }
-    jsize numCols = array[0].row.size();
-    if (numCols <= 1) {
-        // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
-        return NULL;
-    }
-
-    // Allocate array of double arrays
-    jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
-
-    // Create each double array
-    for (uint8_t i = 0; i < numRows; i++) {
-        jdoubleArray doubleArray = env->NewDoubleArray(numCols);
-        env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
-        env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
-        env->DeleteLocalRef(doubleArray);
-    }
-    return returnArray;
-}
-
-jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
-        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
-    jobject arrayList = env->NewObject(class_arrayList,
-                                       method_arrayListCtor); // Create new ArrayList instance
-
-    for (auto gnssAntennaInfo : gnssAntennaInfos) {
-        jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
-
-        env->CallBooleanMethod(arrayList, method_arrayListAdd,
-                               gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
-
-        // Delete Local Refs
-        env->DeleteLocalRef(gnssAntennaInfoObject);
-    }
-    return arrayList;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
-        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
-    jobject phaseCenterOffset =
-            env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
-                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
-
-    return phaseCenterOffset;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
-        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
-    if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
-        gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
-        return NULL;
-    }
-
-    jobjectArray phaseCenterVariationCorrectionsArray =
-            translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
-    jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
-            translate2dDoubleArray(env,
-                                   gnssAntennaInfo
-                                           .phaseCenterVariationCorrectionUncertaintyMillimeters);
-
-    if (phaseCenterVariationCorrectionsArray == NULL ||
-        phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
-        return NULL;
-    }
-
-    jobject phaseCenterVariationCorrections =
-            env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
-                           phaseCenterVariationCorrectionsArray,
-                           phaseCenterVariationCorrectionsUncertaintiesArray);
-
-    env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
-    env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
-
-    return phaseCenterVariationCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
-        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
-    if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
-        gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
-        return NULL;
-    }
-    jobjectArray signalGainCorrectionsArray =
-            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
-    jobjectArray signalGainCorrectionsUncertaintiesArray =
-            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
-
-    if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
-        return NULL;
-    }
-
-    jobject signalGainCorrections =
-            env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
-                           signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
-
-    env->DeleteLocalRef(signalGainCorrectionsArray);
-    env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
-
-    return signalGainCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
-        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
-    jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
-
-    // Nullable
-    jobject phaseCenterVariationCorrections =
-            translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
-
-    // Nullable
-    jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
-
-    // Get builder
-    jobject gnssAntennaInfoBuilderObject =
-            env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
-
-    // Set fields
-    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
-                          method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
-                          gnssAntennaInfo.carrierFrequencyMHz);
-    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
-                          method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
-    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
-                          method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
-                          phaseCenterVariationCorrections);
-    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
-                          method_gnssAntennaInfoBuilderSetSignalGainCorrections,
-                          signalGainCorrections);
-
-    // build
-    jobject gnssAntennaInfoObject =
-            env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
-
-    // Delete Local Refs
-    env->DeleteLocalRef(phaseCenterOffset);
-    env->DeleteLocalRef(phaseCenterVariationCorrections);
-    env->DeleteLocalRef(signalGainCorrections);
-
-    return gnssAntennaInfoObject;
-}
-
-void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
-        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
-    JNIEnv* env = getJniEnv();
-
-    jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
-
-    reportAntennaInfo(env, arrayList);
-
-    env->DeleteLocalRef(arrayList);
-}
-
-void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
-    env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-/*
  * MeasurementCorrectionsCallback implements callback methods of interface
  * IMeasurementCorrectionsCallback.hal.
  */
@@ -1515,7 +1291,6 @@
             "(II)V");
     method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
             "(II)V");
-    method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
     method_reportNavigationMessages = env->GetMethodID(
             clazz,
             "reportNavigationMessage",
@@ -1589,39 +1364,6 @@
     method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
     method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
 
-    jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
-    class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
-    method_gnssAntennaInfoBuilderCtor =
-            env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
-    method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
-            env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
-                             "(D)Landroid/location/GnssAntennaInfo$Builder;");
-    method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
-            env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
-                             "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
-                             "Landroid/location/GnssAntennaInfo$Builder;");
-    method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
-            env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
-                             "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
-                             "Landroid/location/GnssAntennaInfo$Builder;");
-    method_gnssAntennaInfoBuilderSetSignalGainCorrections =
-            env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
-                             "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
-                             "Landroid/location/GnssAntennaInfo$Builder;");
-    method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
-                                                          "()Landroid/location/GnssAntennaInfo;");
-
-    jclass phaseCenterOffsetClass =
-            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
-    class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
-    method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
-
-    jclass sphericalCorrectionsClass =
-            env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
-    class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
-    method_sphericalCorrectionsCtor =
-            env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
-
     jclass gnssPowerStatsClass = env->FindClass("com/android/server/location/gnss/GnssPowerStats");
     class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
     method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
@@ -1634,16 +1376,9 @@
     class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
     method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
 
-    jclass arrayListClass = env->FindClass("java/util/ArrayList");
-    class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
-    method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
-    method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
-
-    jclass doubleArrayClass = env->FindClass("[D");
-    class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
-
     gnss::GnssConfiguration_class_init_once(env);
     gnss::GnssMeasurement_class_init_once(env, clazz);
+    gnss::GnssAntennaInfo_class_init_once(env, clazz);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -2626,7 +2361,7 @@
         return JNI_FALSE;
     }
 
-    sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback();
+    sp<gnss::GnssAntennaInfoCallback> cbIface = new gnss::GnssAntennaInfoCallback(mCallbacksObj);
 
     auto result = gnssAntennaInfoIface->setCallback(cbIface);
 
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
new file mode 100644
index 0000000..444cb8b
--- /dev/null
+++ b/services/core/jni/gnss/Android.bp
@@ -0,0 +1,51 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_library_shared {
+    name: "libservices.core-gnss",
+    defaults: ["libservices.core-gnss-libs"],
+
+    cpp_std: "c++2a",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-DGL_GLEXT_PROTOTYPES",
+    ],
+
+    srcs: [
+        "GnssAntennaInfoCallback.cpp",
+        "GnssConfiguration.cpp",
+        "GnssMeasurement.cpp",
+        "GnssMeasurementCallback.cpp",
+        "Utils.cpp",
+    ],
+}
+
+cc_defaults {
+    name: "libservices.core-gnss-libs",
+    shared_libs: [
+        "libandroid_runtime",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libnativehelper",
+        "libutils",
+        "android.hardware.gnss-V1-cpp",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss@1.1",
+        "android.hardware.gnss@2.0",
+        "android.hardware.gnss@2.1",
+        "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss.visibility_control@1.0",
+    ],
+}
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
new file mode 100644
index 0000000..fbc000b
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "GnssAntInfoCbJni"
+
+#include "GnssAntennaInfoCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
+
+namespace {
+jclass class_gnssAntennaInfoBuilder;
+jclass class_phaseCenterOffset;
+jclass class_sphericalCorrections;
+jclass class_arrayList;
+jclass class_doubleArray;
+
+jmethodID method_reportAntennaInfo;
+jmethodID method_gnssAntennaInfoBuilderCtor;
+jmethodID method_phaseCenterOffsetCtor;
+jmethodID method_sphericalCorrectionsCtor;
+jmethodID method_arrayListCtor;
+jmethodID method_arrayListAdd;
+jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
+jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
+jmethodID method_gnssAntennaInfoBuilderBuild;
+} // anonymous namespace
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz) {
+    method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
+    jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
+    class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
+    method_gnssAntennaInfoBuilderCtor =
+            env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
+    method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
+            env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
+                             "(D)Landroid/location/GnssAntennaInfo$Builder;");
+    method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
+            env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
+                             "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
+                             "Landroid/location/GnssAntennaInfo$Builder;");
+    method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
+            env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
+                             "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+                             "Landroid/location/GnssAntennaInfo$Builder;");
+    method_gnssAntennaInfoBuilderSetSignalGainCorrections =
+            env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
+                             "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+                             "Landroid/location/GnssAntennaInfo$Builder;");
+    method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
+                                                          "()Landroid/location/GnssAntennaInfo;");
+
+    jclass phaseCenterOffsetClass =
+            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
+    class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
+    method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
+
+    jclass sphericalCorrectionsClass =
+            env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
+    class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
+    method_sphericalCorrectionsCtor =
+            env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
+
+    jclass arrayListClass = env->FindClass("java/util/ArrayList");
+    class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+    method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+    method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+    jclass doubleArrayClass = env->FindClass("[D");
+    class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
+}
+
+Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    translateAndReportGnssAntennaInfo(gnssAntennaInfos);
+    return Void();
+}
+
+jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
+    jsize numRows = array.size();
+    if (numRows == 0) {
+        // Empty array
+        return NULL;
+    }
+    jsize numCols = array[0].row.size();
+    if (numCols <= 1) {
+        // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
+        return NULL;
+    }
+
+    // Allocate array of double arrays
+    jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
+
+    // Create each double array
+    for (uint8_t i = 0; i < numRows; i++) {
+        jdoubleArray doubleArray = env->NewDoubleArray(numCols);
+        env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
+        env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
+        env->DeleteLocalRef(doubleArray);
+    }
+    return returnArray;
+}
+
+jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    jobject arrayList = env->NewObject(class_arrayList,
+                                       method_arrayListCtor); // Create new ArrayList instance
+
+    for (auto gnssAntennaInfo : gnssAntennaInfos) {
+        jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
+
+        env->CallBooleanMethod(arrayList, method_arrayListAdd,
+                               gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
+
+        // Delete Local Refs
+        env->DeleteLocalRef(gnssAntennaInfoObject);
+    }
+    return arrayList;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffset =
+            env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
+
+    return phaseCenterOffset;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
+        gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
+        return NULL;
+    }
+
+    jobjectArray phaseCenterVariationCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
+    jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env,
+                                   gnssAntennaInfo
+                                           .phaseCenterVariationCorrectionUncertaintyMillimeters);
+
+    if (phaseCenterVariationCorrectionsArray == NULL ||
+        phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject phaseCenterVariationCorrections =
+            env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+                           phaseCenterVariationCorrectionsArray,
+                           phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    return phaseCenterVariationCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
+        gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
+        return NULL;
+    }
+    jobjectArray signalGainCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
+    jobjectArray signalGainCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
+
+    if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject signalGainCorrections =
+            env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+                           signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(signalGainCorrectionsArray);
+    env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
+
+    return signalGainCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject phaseCenterVariationCorrections =
+            translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
+
+    // Get builder
+    jobject gnssAntennaInfoBuilderObject =
+            env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
+
+    // Set fields
+    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+                          method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
+                          gnssAntennaInfo.carrierFrequencyMHz);
+    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+                          method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
+    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+                          method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
+                          phaseCenterVariationCorrections);
+    env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+                          method_gnssAntennaInfoBuilderSetSignalGainCorrections,
+                          signalGainCorrections);
+
+    // build
+    jobject gnssAntennaInfoObject =
+            env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
+
+    // Delete Local Refs
+    env->DeleteLocalRef(phaseCenterOffset);
+    env->DeleteLocalRef(phaseCenterVariationCorrections);
+    env->DeleteLocalRef(signalGainCorrections);
+
+    return gnssAntennaInfoObject;
+}
+
+void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    JNIEnv* env = getJniEnv();
+
+    jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
+
+    reportAntennaInfo(env, arrayList);
+
+    env->DeleteLocalRef(arrayList);
+}
+
+void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
+    env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.h b/services/core/jni/gnss/GnssAntennaInfoCallback.h
new file mode 100644
index 0000000..0fc7633
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_GNSSANTENNAINFOCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
+#include <log/log.h>
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz);
+
+/*
+ * GnssAntennaInfoCallback implements the callback methods required for the
+ * GnssAntennaInfo interface.
+ */
+struct GnssAntennaInfoCallback : public android::hardware::gnss::V2_1::IGnssAntennaInfoCallback {
+    GnssAntennaInfoCallback(jobject& callbacksObj) : mCallbacksObj(callbacksObj) {}
+    // Methods from V2_1::GnssAntennaInfoCallback follow.
+    hardware::Return<void> gnssAntennaInfoCb(
+            const hardware::hidl_vec<
+                    android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+                    gnssAntennaInfos);
+
+private:
+    jobject translateAllGnssAntennaInfos(
+            JNIEnv* env,
+            const hardware::hidl_vec<
+                    android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+                    gnssAntennaInfos);
+    jobject translateSingleGnssAntennaInfo(
+            JNIEnv* env,
+            const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+                    gnssAntennaInfo);
+    jobject translatePhaseCenterOffset(
+            JNIEnv* env,
+            const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+                    gnssAntennaInfo);
+    jobject translatePhaseCenterVariationCorrections(
+            JNIEnv* env,
+            const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+                    gnssAntennaInfo);
+    jobject translateSignalGainCorrections(
+            JNIEnv* env,
+            const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+                    gnssAntennaInfo);
+    jobjectArray translate2dDoubleArray(
+            JNIEnv* env,
+            const hardware::hidl_vec<android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Row>&
+                    array);
+    void translateAndReportGnssAntennaInfo(
+            const hardware::hidl_vec<
+                    android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+                    gnssAntennaInfos);
+    void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
+
+    jobject& mCallbacksObj;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 8c6c673..34ae469 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -28,6 +28,7 @@
 using hardware::gnss::GnssMeasurement;
 using hardware::gnss::SatellitePvt;
 
+namespace {
 jclass class_arrayList;
 jclass class_clockInfo;
 jclass class_correlationVectorBuilder;
@@ -63,6 +64,8 @@
 jmethodID method_velocityEcef;
 jmethodID method_clockInfo;
 
+} // anonymous namespace
+
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
     method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
                                                     "(Landroid/location/GnssMeasurementsEvent;)V");
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 26f1243..32200fd 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -34,6 +34,7 @@
 
 namespace android::gnss {
 
+namespace {
 extern jclass class_gnssMeasurementsEvent;
 extern jclass class_gnssMeasurement;
 extern jclass class_gnssClock;
@@ -42,6 +43,7 @@
 extern jmethodID method_gnssClockCtor;
 extern jmethodID method_gnssMeasurementCtor;
 extern jmethodID method_reportMeasurementData;
+} // anonymous namespace
 
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
 
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 20e14a9..8cbdfb8 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -18,6 +18,12 @@
 
 #include "Utils.h"
 
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+JavaVM* android::ScopedJniThreadAttach::sJvm;
+
 namespace android {
 
 namespace {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 193d92a..7efe9d4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4567,7 +4567,9 @@
     public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle,
             boolean deviceWideOnly) {
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+                && (isSystemUid(caller) || hasCallingOrSelfPermission(
+                permission.SET_INITIAL_LOCK)));
         return getPasswordMinimumMetricsUnchecked(userHandle, deviceWideOnly);
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index c29de90..939a3dc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -389,9 +389,15 @@
             mCriticalLevelLogged = false;
             Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
         }
-        if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
-                + " with ids " + mPendingLogs.get(0).getId()
-                + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+        if (DEBUG) {
+            if (mPendingLogs.size() > 0) {
+                Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
+                        + " with ids " + mPendingLogs.get(0).getId()
+                        + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+            } else {
+                Slog.d(TAG, "0 pending events in the buffer after merging");
+            }
+        }
     }
 
     @GuardedBy("mLock")
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 91d4f7e..787f588 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
 import com.android.server.attention.AttentionManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.AuthService;
@@ -130,8 +131,6 @@
 import com.android.server.incident.IncidentCompanionService;
 import com.android.server.input.InputManagerService;
 import com.android.server.inputmethod.InputMethodManagerService;
-import com.android.server.inputmethod.InputMethodSystemProperty;
-import com.android.server.inputmethod.MultiClientInputMethodManagerService;
 import com.android.server.integrity.AppIntegrityManagerService;
 import com.android.server.lights.LightsService;
 import com.android.server.location.LocationManagerService;
@@ -377,6 +376,8 @@
             "com.android.server.connectivity.IpConnectivityMetrics";
     private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
             "com.android.server.media.MediaCommunicationService";
+    private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS =
+            "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle";
 
     private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
     private static final String GAME_MANAGER_SERVICE_CLASS =
@@ -1595,12 +1596,7 @@
         // Bring up services needed for UI.
         if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
             t.traceBegin("StartInputMethodManagerLifecycle");
-            if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
-                mSystemServiceManager.startService(
-                        MultiClientInputMethodManagerService.Lifecycle.class);
-            } else {
-                mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
-            }
+            mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
             t.traceEnd();
 
             t.traceBegin("StartAccessibilityManagerService");
@@ -2070,11 +2066,14 @@
             mSystemServiceManager.startService(DockObserver.class);
             t.traceEnd();
 
+            // TODO(b/191495635): re-enable thermal observer after fixing b/191375904.
+            /*
             if (isWatch) {
                 t.traceBegin("StartThermalObserver");
                 mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
                 t.traceEnd();
             }
+            */
 
             t.traceBegin("StartWiredAccessoryManager");
             try {
@@ -2428,7 +2427,9 @@
             t.traceEnd();
         }
 
-        if (isWatch) {
+        // TODO(b/191495635): Re-enable these services after fixing b/191375904.
+       /*
+       if (isWatch) {
             // Must be started before services that depend it, e.g. WearConnectivityService
             t.traceBegin("StartWearPowerService");
             mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS);
@@ -2456,6 +2457,7 @@
             mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS);
             t.traceEnd();
         }
+        */
 
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
             t.traceBegin("StartSliceManagerService");
@@ -2632,6 +2634,10 @@
         mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        t.traceBegin("ArtManagerLocal");
+        LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+        t.traceEnd();
+
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
             t.traceBegin("UwbService");
             mSystemServiceManager.startService(UWB_SERVICE_CLASS);
@@ -2650,6 +2656,10 @@
         mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
         t.traceEnd();
 
+        t.traceBegin("AppCompatOverridesService");
+        mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
+        t.traceEnd();
+
         ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
                 START_BLOB_STORE_SERVICE);
 
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index e3e2708..fdf23d3 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -59,9 +59,7 @@
 
     private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
 
-    private static final long BG_PROCESS_PERIOD = DEBUG
-            ? TimeUnit.MINUTES.toMillis(1)
-            : TimeUnit.DAYS.toMillis(1);
+    private static final long BG_PROCESS_PERIOD = TimeUnit.DAYS.toMillis(1); // every 1 day.
 
     private IProfCollectd mIProfcollect;
     private static ProfcollectForwardingService sSelfService;
@@ -286,6 +284,11 @@
         updateEngine.bind(new UpdateEngineCallback() {
             @Override
             public void onStatusUpdate(int status, float percent) {
+                if (DEBUG) {
+                    Log.d(LOG_TAG, "Received OTA status update, status: " + status + ", percent: "
+                            + percent);
+                }
+
                 if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) {
                     packProfileReport();
                 }
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index aca48b6..ee5a534 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -19,8 +19,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.os.Build;
 import android.os.Environment;
diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp
new file mode 100644
index 0000000..35d754b
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "PackageInstallerTests",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "junit",
+        "kotlin-test",
+        "truth-prebuilt",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/services/tests/PackageManager/packageinstaller/AndroidManifest.xml b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
new file mode 100644
index 0000000..d706258
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.packageinstaller.test">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.packageinstaller.test"
+        />
+
+</manifest>
+
diff --git a/services/tests/PackageManager/packageinstaller/AndroidTest.xml b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
new file mode 100644
index 0000000..c39285ff
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="Test module config for PackageInstallerTests">
+    <option name="test-tag" value="PackageInstallerTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="PackageInstallerTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.packageinstaller.test" />
+    </test>
+</configuration>
diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
new file mode 100644
index 0000000..d7d2726
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.test
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import androidx.test.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+
+class ExportedComponentTest {
+
+    private val context: Context = InstrumentationRegistry.getContext()
+
+    @Test
+    fun verifyNoExportedReceivers() {
+        val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
+            data = Uri.parse("content://mockForTest")
+        }
+        val packageInstallers = context.packageManager.queryIntentActivities(intent,
+            PackageManager.MATCH_DEFAULT_ONLY or PackageManager.MATCH_DISABLED_COMPONENTS)
+            .map { it.activityInfo.packageName }
+            .distinct()
+            .map { context.packageManager.getPackageInfo(it, PackageManager.GET_RECEIVERS) }
+
+        assertThat(packageInstallers).isNotEmpty()
+
+        packageInstallers.forEach {
+            val exported = it.receivers.filter { it.exported }
+            assertWithMessage("Receivers should not be exported").that(exported).isEmpty()
+        }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index 6f168a3..7b821c0 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -32,6 +32,7 @@
     <!-- Load additional APKs onto device -->
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
+        <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
     </target_preparer>
 
     <option name="test-tag" value="AppEnumerationInternalTest" />
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
index 9337845..63aaf24 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
@@ -43,8 +43,16 @@
     private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/";
     private static final String SYNC_PROVIDER_APK_PATH =
             TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk";
-    private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider";
-    private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME;
+    private static final String HAS_APPOP_PERMISSION_APK_PATH =
+            TEST_DATA_PATH + "AppEnumerationHasAppOpPermissionTestApp.apk";
+
+    private static final String TARGET_SYNC_PROVIDER = "com.android.appenumeration.syncprovider";
+    private static final String TARGET_HAS_APPOP_PERMISSION =
+            "com.android.appenumeration.hasappoppermission";
+
+    private static final String SYNC_PROVIDER_AUTHORITY = TARGET_SYNC_PROVIDER;
+    private static final String PERMISSION_REQUEST_INSTALL_PACKAGES =
+            "android.permission.REQUEST_INSTALL_PACKAGES";
 
     private IPackageManager mIPackageManager;
 
@@ -55,7 +63,8 @@
 
     @After
     public void tearDown() throws Exception {
-        uninstallPackage(SYNC_PROVIDER_PKG_NAME);
+        uninstallPackage(TARGET_SYNC_PROVIDER);
+        uninstallPackage(TARGET_HAS_APPOP_PERMISSION);
     }
 
     @Test
@@ -67,7 +76,7 @@
 
         assertThat(names).contains(SYNC_PROVIDER_AUTHORITY);
         assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
-                .contains(SYNC_PROVIDER_PKG_NAME);
+                .contains(TARGET_SYNC_PROVIDER);
     }
 
     @Test
@@ -79,7 +88,27 @@
 
         assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY);
         assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
-                .doesNotContain(SYNC_PROVIDER_PKG_NAME);
+                .doesNotContain(TARGET_SYNC_PROVIDER);
+    }
+
+    @Test
+    public void getAppOpPermissionPackages_canSeeForceQueryable() throws Exception {
+        installPackage(HAS_APPOP_PERMISSION_APK_PATH, true /* forceQueryable */);
+
+        final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+                PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+        assertThat(packageNames).asList().contains(TARGET_HAS_APPOP_PERMISSION);
+    }
+
+    @Test
+    public void getAppOpPermissionPackages_cannotSeeHasAppOpPermission() throws Exception {
+        installPackage(HAS_APPOP_PERMISSION_APK_PATH, false /* forceQueryable */);
+
+        final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+                PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+        assertThat(packageNames).asList().doesNotContain(TARGET_HAS_APPOP_PERMISSION);
     }
 
     private static void installPackage(String apkPath, boolean forceQueryable) {
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
index 64239b4..7aa300b 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
@@ -34,3 +34,17 @@
     test_suites: ["device-tests"],
     platform_apis: true,
 }
+
+android_test_helper_app {
+    name: "AppEnumerationHasAppOpPermissionTestApp",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest-hasAppOpPermission.xml",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    test_suites: ["device-tests"],
+    platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml
new file mode 100644
index 0000000..be8a6dc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT 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.appenumeration.hasappoppermission">
+
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 022fadc..15dfd26 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -21,12 +21,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManagerInternal;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -48,7 +51,10 @@
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
-import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -62,6 +68,8 @@
  */
 @RunWith(MockitoJUnitRunner.class)
 public class CacheOomRankerTest {
+    private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
+            ZoneOffset.UTC).toInstant();
 
     @Mock
     private AppOpsService mAppOpsService;
@@ -136,6 +144,15 @@
 
         mExecutor.init();
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+                Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
+                false);
+        mExecutor.waitForLatch();
+        assertThat(mCacheOomRanker.mPreserveTopNApps)
+                .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
+
+        mExecutor.init();
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
                 Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f),
                 false);
@@ -165,6 +182,7 @@
     @Test
     public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 5,
+                /* preserveTopNApps= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */1.0f);
@@ -172,36 +190,38 @@
         ProcessList list = new ProcessList();
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(lastUsed40MinutesAgo);
         ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(lastUsed42MinutesAgo);
         ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
         processList.add(lastUsed60MinutesAgo);
         ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(lastUsed15MinutesAgo);
         ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
         processList.add(lastUsed17MinutesAgo);
         // Only re-ranking 5 entries so this should stay in most recent position.
         ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
         processList.add(lastUsed30MinutesAgo);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 5 ordered by least recently used first, then last processes position unchanged.
         assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
                 lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
-                lastUsed30MinutesAgo);
+                lastUsed30MinutesAgo).inOrder();
     }
 
     @Test
     public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 1.0f,
                 /* lruWeight= */ 0.0f);
@@ -209,145 +229,328 @@
         ProcessList list = new ProcessList();
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(rss10k);
         ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(rss20k);
         ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
         processList.add(rss1k);
         ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(rss100k);
         ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
         processList.add(rss2k);
         ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
         processList.add(rss15k);
         // Only re-ranking 6 entries so this should stay in most recent position.
         ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
         processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 6 ordered by largest pss, then last processes position unchanged.
         assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
-                rss16k);
+                rss16k).inOrder();
     }
 
     @Test
     public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.setLruProcessServiceStartLSP(1);
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(used1000);
         ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(used2000);
         ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(used10);
         ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
         processList.add(used20);
         // Only re-ranking 6 entries so last two should stay in most recent position.
         ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
         processList.add(used500);
         ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
         processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 4 ordered by uses, then last processes position unchanged.
         assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
-                used200);
+                used200).inOrder();
     }
 
     @Test
-    public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
+    public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
-                /* usesWeight= */ 0.5f,
-                /* pssWeight= */ 0.2f,
-                /* lruWeight= */ 0.3f);
-
-        ProcessList list = new ProcessList();
-        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
-        ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
-        processList.add(unknownAdj1);
-        ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
-        processList.add(unknownAdj2);
-        ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
-        processList.add(unknownAdj3);
-        ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
-        processList.add(foregroundAdj);
-        ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
-        processList.add(serviceAdj);
-        ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
-        processList.add(systemAdj);
-
-        // 6 Processes but only 3 in eligible for cache so no re-ranking.
-        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
-
-        // All positions unchanged.
-        assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
-                foregroundAdj, serviceAdj, systemAdj);
-    }
-
-    @Test
-    public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
-        setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.setLruProcessServiceStartLSP(4);
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(used1000);
         ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(used2000);
         ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(used10);
-        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
-        processList.add(used20);
-        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
-        processList.add(used500);
-        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
-        processList.add(used200);
+        ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(foregroundAdj);
+        ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(serviceAdj);
+        ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(systemAdj);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
-        // All positions unchanged.
-        assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
-                used200);
+        // 6 processes, only 3 in eligible for cache, so only those are re-ranked.
+        assertThat(processList).containsExactly(used10, used1000, used2000,
+                foregroundAdj, serviceAdj, systemAdj).inOrder();
     }
 
-    private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
+    @Test
+    public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(service1);
+        ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(service2);
+        ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(service3);
+        list.setLruProcessServiceStartLSP(3);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // Services unchanged, rest re-ranked.
+        assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
+                service3).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList set1List = new ProcessList();
+        ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
+        ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        set1ProcessList.add(set1Used1000);
+        ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        set1ProcessList.add(set1Used2000);
+        ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        set1ProcessList.add(set1Used10);
+        ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        set1ProcessList.add(set1Uses20);
+        ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        set1ProcessList.add(set1Uses500);
+        ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        set1ProcessList.add(set1Uses200);
+        set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
+                set1List.getLruProcessServiceStartLOSP());
+        assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
+                set1Uses500, set1Used1000, set1Used2000).inOrder();
+
+        ProcessList set2List = new ProcessList();
+        ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
+        ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        set2ProcessList.add(set2Used1000);
+        ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        set2ProcessList.add(set2Used2000);
+        ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        set2ProcessList.add(set2Used10);
+        ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        set2ProcessList.add(set2ForegroundAdj);
+        ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        set2ProcessList.add(set2ServiceAdj);
+        ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        set2ProcessList.add(set2SystemAdj);
+        set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
+                set2List.getLruProcessServiceStartLOSP());
+        assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
+                set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 3,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        // Preserving the top 3 processes, so these should not be re-ranked.
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // First 3 ordered by uses, then last processes position unchanged.
+        assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+                used200).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 100,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // Nothing reordered, as we preserve the top 100 apps.
+        assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
+                used200).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ -100,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
+        assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+                used200).inOrder();
+    }
+
+    private void setConfig(int numberToReRank, int preserveTopNApps, float usesWeight,
+            float pssWeight, float lruWeight)
             throws InterruptedException {
         mExecutor.init(4);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -355,6 +558,10 @@
                 Integer.toString(numberToReRank),
                 false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+                Integer.toString(preserveTopNApps),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
                 Float.toString(lruWeight),
                 false);
@@ -364,12 +571,12 @@
                 false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
-                Float.toString(useWeight),
+                Float.toString(usesWeight),
                 false);
         mExecutor.waitForLatch();
         assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
         assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
-        assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
+        assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
         assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
     }
 
@@ -382,7 +589,7 @@
         app.info.uid = mNextPackageUid++;
         // Exact value does not mater, it can be any state for which compaction is allowed.
         app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-        app.mState.setSetAdj(setAdj);
+        app.mState.setCurAdj(setAdj);
         app.setLastActivityTime(lastActivityTime);
         app.mProfile.setLastRss(lastRss);
         app.mState.setCached(false);
@@ -390,6 +597,12 @@
             app.mState.setCached(false);
             app.mState.setCached(true);
         }
+        // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
+        // app is currently launching.
+        ProcessStatsService processStatsService = new ProcessStatsService(
+                mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
+                "procstats"));
+        app.makeActive(mock(IApplicationThread.class), processStatsService);
         return app;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 4d86c87..8840057 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -182,7 +182,14 @@
     }
 
     private void mockDeviceConfigPerformance() {
-        String configString = "mode=2,downscaleFactor=0.5";
+        String configString = "mode=2,downscaleFactor=0.5,useAngle=false";
+        when(DeviceConfig.getProperty(anyString(), anyString()))
+                .thenReturn(configString);
+    }
+
+    // ANGLE will be disabled for most apps, so treat enabling ANGLE as a special case.
+    private void mockDeviceConfigPerformanceEnableAngle() {
+        String configString = "mode=2,downscaleFactor=0.5,useAngle=true";
         when(DeviceConfig.getProperty(anyString(), anyString()))
                 .thenReturn(configString);
     }
@@ -212,7 +219,8 @@
     }
 
     private void mockGameModeOptInAll() throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
         Bundle metaDataBundle = new Bundle();
         metaDataBundle.putBoolean(
                 GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -224,7 +232,8 @@
     }
 
     private void mockGameModeOptInPerformance() throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
         Bundle metaDataBundle = new Bundle();
         metaDataBundle.putBoolean(
                 GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -234,7 +243,8 @@
     }
 
     private void mockGameModeOptInBattery() throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
         Bundle metaDataBundle = new Bundle();
         metaDataBundle.putBoolean(
                 GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true);
@@ -244,7 +254,8 @@
     }
 
     private void mockInterventionAllowDownscaleTrue() throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
         Bundle metaDataBundle = new Bundle();
         metaDataBundle.putBoolean(
                 GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true);
@@ -254,7 +265,8 @@
     }
 
     private void mockInterventionAllowDownscaleFalse() throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
         Bundle metaDataBundle = new Bundle();
         metaDataBundle.putBoolean(
                 GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false);
@@ -263,6 +275,27 @@
                 .thenReturn(applicationInfo);
     }
 
+    private void mockInterventionAllowAngleTrue() throws Exception {
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+        Bundle metaDataBundle = new Bundle();
+        metaDataBundle.putBoolean(
+                GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, true);
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+    }
+
+    private void mockInterventionAllowAngleFalse() throws Exception {
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+        Bundle metaDataBundle = new Bundle();
+        metaDataBundle.putBoolean(
+                GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, false);
+        applicationInfo.metaData = metaDataBundle;
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+    }
+
     /**
      * By default game mode is not supported.
      */
@@ -427,6 +460,19 @@
         assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
     }
 
+    private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
+            boolean angleEnabled) {
+        gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+
+        // Validate GamePackageConfiguration returns the correct value.
+        GameManagerService.GamePackageConfiguration config =
+                gameManagerService.getConfig(mPackageName);
+        assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
+
+        // Validate GameManagerService.getAngleEnabled() returns the correct value.
+        assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
+    }
+
     /**
      * Phenotype device config exists, but is only propagating the default value.
      */
@@ -592,6 +638,50 @@
     }
 
     /**
+     * PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata.
+     */
+    @Test
+    public void testInterventionAllowAngleDefault() throws Exception {
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+        mockDeviceConfigPerformance();
+        mockModifyGameModeGranted();
+        checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+    }
+
+    /**
+     * PERFORMANCE game mode is configured through Phenotype. The app has opted-out of ANGLE.
+     */
+    @Test
+    public void testInterventionAllowAngleFalse() throws Exception {
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+        mockDeviceConfigPerformanceEnableAngle();
+        mockInterventionAllowAngleFalse();
+        mockModifyGameModeGranted();
+        checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+    }
+
+    /**
+     * PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified
+     * the ANGLE metadata default value of "true".
+     */
+    @Test
+    public void testInterventionAllowAngleTrue() throws Exception {
+        mockDeviceConfigPerformanceEnableAngle();
+        mockInterventionAllowAngleTrue();
+
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+        mockModifyGameModeGranted();
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+        checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
+    }
+
+    /**
      * PERFORMANCE game mode is configured through Phenotype, but the app has also opted into the
      * same mode. No interventions for this game mode should be available in this case.
      */
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
new file mode 100644
index 0000000..24f6b0b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
@@ -0,0 +1 @@
+include /apex/appsearch/OWNERS
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
new file mode 100644
index 0000000..f8c3520
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
new file mode 100644
index 0000000..bf97042
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test class for {@link AppCompatOverridesParser}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesParserTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesParserTest {
+    private static final String PACKAGE_1 = "com.android.test1";
+    private static final String PACKAGE_2 = "com.android.test2";
+    private static final String PACKAGE_3 = "com.android.test3";
+    private static final String PACKAGE_4 = "com.android.test4";
+
+    private AppCompatOverridesParser mParser;
+
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mParser = new AppCompatOverridesParser(mPackageManager);
+    }
+
+    @Test
+    public void parseRemoveOverrides_emptyConfig_returnsEmpty() {
+        Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+
+        assertThat(mParser.parseRemoveOverrides("", ownedChangeIds)).isEmpty();
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasWildcardNoOwnedChangeIds_returnsEmpty() {
+        when(mPackageManager.getInstalledApplications(anyInt()))
+                .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+        assertThat(mParser.parseRemoveOverrides("*", /* ownedChangeIds= */ emptySet())).isEmpty();
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasWildcard_returnsAllInstalledPackagesToAllOwnedIds() {
+        Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+        when(mPackageManager.getInstalledApplications(anyInt()))
+                .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2),
+                        createAppInfo(PACKAGE_3)));
+
+        Map<String, Set<Long>> result = mParser.parseRemoveOverrides("*", ownedChangeIds);
+
+        assertThat(result).hasSize(3);
+        assertThat(result.get(PACKAGE_1)).containsExactly(123L, 456L);
+        assertThat(result.get(PACKAGE_2)).containsExactly(123L, 456L);
+        assertThat(result.get(PACKAGE_3)).containsExactly(123L, 456L);
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasInvalidWildcardSymbol_returnsEmpty() {
+        Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+        when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+                Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+        assertThat(mParser.parseRemoveOverrides("**", ownedChangeIds)).isEmpty();
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasSingleEntry_returnsPackageToChangeIds() {
+        Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+                PACKAGE_1 + "=12:34", /* ownedChangeIds= */ emptySet());
+
+        assertThat(result).hasSize(1);
+        assertThat(result.get(PACKAGE_1)).containsExactly(12L, 34L);
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasMultipleEntries_returnsPackagesToChangeIds() {
+        Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L, 56L, 78L));
+
+        Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+                PACKAGE_1 + "=12," + PACKAGE_2 + "=*," + PACKAGE_3 + "=12:56:78," + PACKAGE_4
+                        + "=", ownedChangeIds);
+
+        assertThat(result).hasSize(3);
+        assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+        assertThat(result.get(PACKAGE_2)).containsExactly(12L, 34L, 56L, 78L);
+        assertThat(result.get(PACKAGE_3)).containsExactly(12L, 56L, 78L);
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasPackageWithWildcardNoOwnedId_returnsWithoutPackage() {
+        Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+                PACKAGE_1 + "=*," + PACKAGE_2 + "=12", /* ownedChangeIds= */ emptySet());
+
+        assertThat(result).hasSize(1);
+        assertThat(result.get(PACKAGE_2)).containsExactly(12L);
+    }
+
+    @Test
+    public void parseRemoveOverrides_configHasInvalidKeyValueListFormat_returnsEmpty() {
+        Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L));
+
+        assertThat(mParser.parseRemoveOverrides(
+                PACKAGE_1 + "=12," + PACKAGE_2 + ">34", ownedChangeIds)).isEmpty();
+    }
+
+
+    @Test
+    public void parseRemoveOverrides_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+        Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+                PACKAGE_1 + "=12," + PACKAGE_2 + "=12:56L:78," + PACKAGE_3
+                        + "=34L", /* ownedChangeIds= */ emptySet());
+
+        assertThat(result).hasSize(2);
+        assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+        assertThat(result.get(PACKAGE_2)).containsExactly(12L, 78L);
+    }
+
+    @Test
+    public void parseOwnedChangeIds_emptyConfig_returnsEmpty() {
+        assertThat(AppCompatOverridesParser.parseOwnedChangeIds("")).isEmpty();
+    }
+
+    @Test
+    public void parseOwnedChangeIds_configHasSingleChangeId_returnsChangeId() {
+        assertThat(AppCompatOverridesParser.parseOwnedChangeIds("123")).containsExactly(123L);
+    }
+
+    @Test
+    public void parseOwnedChangeIds_configHasMultipleChangeIds_returnsChangeIds() {
+        assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,34,56")).containsExactly(12L,
+                34L, 56L);
+    }
+
+    @Test
+    public void parseOwnedChangeIds_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+        // We add a valid entry before and after the invalid ones to make sure they are applied.
+        assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,C34,56")).containsExactly(12L,
+                56L);
+    }
+
+    @Test
+    public void parsePackageOverrides_emptyConfig_returnsEmpty() {
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+        assertThat(result.overridesToAdd).isEmpty();
+        assertThat(result.overridesToRemove).isEmpty();
+    }
+
+    @Test
+    public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+        assertThat(result.overridesToAdd).hasSize(1);
+        assertThat(result.overridesToAdd.get(123L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(true).build());
+    }
+
+    @Test
+    public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() {
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true,"
+                        + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true,"
+                        + "56:6::true,1112:6:7:false", /* versionCode= */
+                5, /* changeIdsToSkip= */ emptySet());
+
+        assertThat(result.overridesToAdd).hasSize(6);
+        assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(false).build());
+        assertThat(result.overridesToAdd.get(34L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
+                        true).build());
+        assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
+                        false).build());
+        assertThat(result.overridesToAdd.get(78L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
+                        true).build());
+        assertThat(result.overridesToAdd.get(910L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
+        assertThat(result.overridesToAdd.get(1112L)).isEqualTo(
+                new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
+        assertThat(result.overridesToRemove).isEmpty();
+    }
+
+    @Test
+    public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() {
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+        assertThat(result.overridesToRemove).containsExactly(12L, 34L);
+        assertThat(result.overridesToAdd).isEmpty();
+    }
+
+    @Test
+    public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() {
+        // Note that change 56 is both added and removed, therefore it will only be removed.
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */
+                emptySet());
+
+        assertThat(result.overridesToAdd).hasSize(1);
+        assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(true).build());
+        assertThat(result.overridesToRemove).containsExactly(34L, 56L);
+    }
+
+    @Test
+    public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
+        ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+        changeIdsToSkip.add(34L);
+        changeIdsToSkip.add(56L);
+        changeIdsToSkip.add(910L);
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip);
+
+        assertThat(result.overridesToAdd).hasSize(1);
+        assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(true).build());
+        assertThat(result.overridesToRemove).containsExactly(78L);
+    }
+
+    @Test
+    public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
+        ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+        changeIdsToSkip.add(12L);
+        changeIdsToSkip.add(34L);
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip);
+
+        assertThat(result.overridesToAdd).isEmpty();
+        assertThat(result.overridesToRemove).isEmpty();
+    }
+
+    @Test
+    public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
+        // We add a valid entry before and after the invalid ones to make sure they are applied.
+        PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+                "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
+                        + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:",
+                /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+        assertThat(result.overridesToAdd).hasSize(2);
+        assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(true).build());
+        assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled(
+                        false).build());
+        assertThat(result.overridesToRemove).containsExactly(34L);
+    }
+
+    private static ApplicationInfo createAppInfo(String packageName) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = packageName;
+        return appInfo;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
new file mode 100644
index 0000000..3129272
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+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;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Test class for {@link AppCompatOverridesService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesServiceTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesServiceTest {
+    private static final String NAMESPACE_1 = "namespace_1";
+    private static final String NAMESPACE_2 = "namespace_2";
+    private static final String NAMESPACE_3 = "namespace_3";
+    private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(NAMESPACE_1,
+            NAMESPACE_2, NAMESPACE_3);
+
+    private static final String PACKAGE_1 = "com.android.test1";
+    private static final String PACKAGE_2 = "com.android.test2";
+    private static final String PACKAGE_3 = "com.android.test3";
+    private static final String PACKAGE_4 = "com.android.test4";
+    private static final String PACKAGE_5 = "com.android.test5";
+
+    private MockContext mMockContext;
+    private BroadcastReceiver mPackageReceiver;
+    private AppCompatOverridesService mService;
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private IPlatformCompat mPlatformCompat;
+
+    @Captor
+    private ArgumentCaptor<CompatibilityOverrideConfig> mOverridesToAddConfigCaptor;
+    @Captor
+    private ArgumentCaptor<CompatibilityOverridesToRemoveConfig> mOverridesToRemoveConfigCaptor;
+
+    @Rule
+    public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+
+    class MockContext extends ContextWrapper {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public PackageManager getPackageManager() {
+            return mPackageManager;
+        }
+
+        @Override
+        public Executor getMainExecutor() {
+            // Run on current thread
+            return Runnable::run;
+        }
+
+        @Override
+        @Nullable
+        public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
+                @NonNull IntentFilter filter, @Nullable String broadcastPermission,
+                @Nullable Handler scheduler) {
+            mPackageReceiver = receiver;
+            return null;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mMockContext = new MockContext(
+                InstrumentationRegistry.getInstrumentation().getTargetContext());
+        mService = new AppCompatOverridesService(mMockContext, mPlatformCompat,
+                SUPPORTED_NAMESPACES);
+        mService.registerPackageReceiver();
+        assertThat(mPackageReceiver).isNotNull();
+    }
+
+    @Test
+    public void onPropertiesChanged_removeOverridesFlagNotSet_appliesPackageOverrides()
+            throws Exception {
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 3);
+        mockGetApplicationInfoNotInstalled(PACKAGE_2);
+        mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10);
+        mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1);
+        mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1);
+
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true")
+                .setString(PACKAGE_2, "123:::true")
+                .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::")
+                .setString(PACKAGE_4, "")
+                .setString(PACKAGE_5, "123:::,789:::")
+                .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build());
+
+        Map<Long, PackageOverride> addedOverrides;
+        // Package 1
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+        addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+        assertThat(addedOverrides).hasSize(2);
+        assertThat(addedOverrides.get(123L)).isEqualTo(
+                new PackageOverride.Builder().setEnabled(true).build());
+        assertThat(addedOverrides.get(456L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
+        // Package 2
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+        // Package 3
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_3));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+        addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+        assertThat(addedOverrides).hasSize(1);
+        assertThat(addedOverrides.get(123L)).isEqualTo(
+                new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode(
+                        11).setEnabled(false).build());
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+        // Package 4
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+        // Package 5
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_5));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5));
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
+    }
+
+    @Test
+    public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123")
+                .setString(PACKAGE_1, "123:::true")
+                .setString(PACKAGE_4, "123:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+                .setString(PACKAGE_2, "123:::true")
+                .setString(PACKAGE_3, "456:::true").build());
+
+        // Package 1
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(789L);
+        // Package 2
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+        // Package 3
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_3));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+        // Package 4 (not applied because it hasn't changed after the listener was added)
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+    }
+
+    @Test
+    public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly()
+            throws Exception {
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES,
+                        PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build());
+
+        // Package 1
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L);
+        // Package 2
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+    }
+
+    @Test
+    public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456")
+                .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+                .setString(PACKAGE_3, "456:::false,789:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789")
+                .setString(PACKAGE_2, "123:::true,456:::").build());
+
+        // Package 1
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L,
+                789L);
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+        // Package 2
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+        verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+        List<CompatibilityOverridesToRemoveConfig> configs =
+                mOverridesToRemoveConfigCaptor.getAllValues();
+        assertThat(configs.size()).isAtLeast(2);
+        assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L);
+        assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L);
+        // Package 3
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_3));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+    }
+
+    @Test
+    public void onPropertiesChanged_ownedChangeIdsFlagAndSomePackageOverrideFlagsChanged_ok()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=*")
+                .setString(FLAG_OWNED_CHANGE_IDS, "123,456")
+                .setString(PACKAGE_1, "123:::true")
+                .setString(PACKAGE_3, "456:::false").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+                .setString(PACKAGE_2, "123:::true").build());
+
+        // Package 1
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+                789L);
+        // Package 2
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+                eq(PACKAGE_2));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
+        // Package 3
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_3));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+    }
+
+    @Test
+    public void onPropertiesChanged_platformCompatThrowsExceptionForSomeCalls_skipsFailedCalls()
+            throws Exception {
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+        mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 0);
+        doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+        doThrow(new RemoteException()).when(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+
+        mService.registerDeviceConfigListeners();
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "123:::true,456:::")
+                .setString(PACKAGE_2, "123:::true,456:::")
+                .setString(PACKAGE_3, "123:::true,456:::")
+                .setString(PACKAGE_4, "123:::true,456:::").build());
+
+        // Package 1
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+                eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+        // Package 2
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+                eq(PACKAGE_2));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+        // Package 3
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+                eq(PACKAGE_3));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+        // Package 4
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+                eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+    }
+
+    @Test
+    public void packageReceiver_packageAddedIntentDataIsNull_doesNothing() throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext, new Intent(ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_actionIsNull_doesNothing() throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, /* action= */ null));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_unsupportedAction_doesNothing() throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_USER_SWITCHED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_packageAddedIntentPackageNotInstalled_doesNothing()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::true").build());
+        mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_packageAddedIntentNoOverridesForPackage_doesNothing()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_2, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_3, "201:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_packageAddedIntent_appliesOverridesFromAllNamespaces()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true,103:::")
+                .setString(PACKAGE_2, "102:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_3, "201:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+                .setString(PACKAGE_1, "301:::true,302:::false")
+                .setString(PACKAGE_2, "302:::false").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, times(2)).putOverridesOnReleaseBuilds(
+                mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+        assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+        assertThat(configs.get(1).overrides.keySet()).containsExactly(301L, 302L);
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+    }
+
+    @Test
+    public void packageReceiver_packageChangedIntent_appliesOverrides()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true,103:::").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_CHANGED));
+
+        verify(mPlatformCompat).putOverridesOnReleaseBuilds(
+                mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L);
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+    }
+
+    @Test
+    public void packageReceiver_packageAddedIntentRemoveOverridesSetForSomeNamespaces_skipsIds()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=103," + PACKAGE_2 + "=101")
+                .setString(PACKAGE_1, "101:::true,103:::")
+                .setString(PACKAGE_2, "102:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+                .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=301," + PACKAGE_3 + "=302")
+                .setString(PACKAGE_1, "301:::true,302:::false,303:::")
+                .setString(PACKAGE_3, "302:::false").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+                mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+        verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+        assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+        assertThat(configs.get(1).overrides.keySet()).containsExactly(201L);
+        assertThat(configs.get(2).overrides.keySet()).containsExactly(302L);
+        assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(303L);
+    }
+
+    @Test
+    public void packageReceiver_packageRemovedIntentNoOverridesForPackage_doesNothing()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+                .setString(PACKAGE_2, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+                .setString(PACKAGE_3, "201:::true").build());
+        mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_packageRemovedIntentPackageInstalledForAnotherUser_doesNothing()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+                .setString(PACKAGE_1, "101:::true,103:::").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+                .setString(PACKAGE_1, "202:::false").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    @Test
+    public void packageReceiver_packageRemovedIntent_removesOwnedOverridesForNamespacesWithPackage()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+                .setString(PACKAGE_1, "101:::true,103:::")
+                .setString(PACKAGE_2, "102:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(FLAG_OWNED_CHANGE_IDS, "201")
+                .setString(PACKAGE_3, "201:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+                .setString(FLAG_OWNED_CHANGE_IDS, "301,302")
+                .setString(PACKAGE_1, "302:::")
+                .setString(PACKAGE_2, "301:::true").build());
+        mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        List<CompatibilityOverridesToRemoveConfig> configs =
+                mOverridesToRemoveConfigCaptor.getAllValues();
+        assertThat(configs.get(0).changeIds).containsExactly(101L, 102L, 103L);
+        assertThat(configs.get(1).changeIds).containsExactly(301L, 302L);
+    }
+
+    @Test
+    public void packageReceiver_packageRemovedIntentNoOwnedIdsForSomeNamespace_skipsNamespace()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+                .setString(FLAG_OWNED_CHANGE_IDS, "301")
+                .setString(PACKAGE_1, "301:::true").build());
+        mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+        verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+                mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+        List<CompatibilityOverridesToRemoveConfig> configs =
+                mOverridesToRemoveConfigCaptor.getAllValues();
+        assertThat(configs.get(0).changeIds).containsExactly(101L, 102L);
+        assertThat(configs.get(1).changeIds).containsExactly(301L);
+    }
+
+    @Test
+    public void packageReceiver_platformCompatThrowsExceptionForSomeNamespace_skipsFailedCall()
+            throws Exception {
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+                .setString(PACKAGE_1, "101:::true").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+                .setString(PACKAGE_1, "201:::false").build());
+        DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+                .setString(PACKAGE_1, "301:::true").build());
+        mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+        doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+                argThat(config -> config.overrides.containsKey(201L)), eq(PACKAGE_1));
+
+        mPackageReceiver.onReceive(mMockContext,
+                createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+        verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+                any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+        verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+                any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+    }
+
+    private void mockGetApplicationInfo(String packageName, long versionCode)
+            throws Exception {
+        when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())).thenReturn(
+                createAppInfo(versionCode));
+    }
+
+    private void mockGetApplicationInfoNotInstalled(String packageName) throws Exception {
+        when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+                .thenThrow(new PackageManager.NameNotFoundException());
+    }
+
+    private static ApplicationInfo createAppInfo(long versionCode) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.longVersionCode = versionCode;
+        return appInfo;
+    }
+
+    private Intent createPackageIntent(String packageName, @Nullable String action) {
+        return new Intent(action, Uri.parse("package:" + packageName));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 0000000..6e8aefc
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/overrides/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index b2471fa..697451e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 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.Mockito.when;
 
@@ -97,11 +98,6 @@
             super(context);
             mAppStateTracker = mock(AppStateTrackerImpl.class);
         }
-
-        @Override
-        public boolean isChainedAttributionEnabled() {
-            return false;
-        }
     }
 
     @Before
@@ -710,7 +706,7 @@
         spyOn(mService);
         doNothing().when(mService).evaluateControllerStatesLocked(any());
         doNothing().when(mService).noteJobsPending(any());
-        doReturn(true).when(mService).isReadyToBeExecutedLocked(any());
+        doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean());
         advanceElapsedClock(24 * HOUR_IN_MILLIS);
 
         JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 3dc7bc1..a9853bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -152,6 +152,11 @@
                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
                         DataUnit.MEBIBYTES.toBytes(1))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+        final JobInfo.Builder jobWithMinChunk = createJob()
+                .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
+                        DataUnit.MEBIBYTES.toBytes(1))
+                .setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100))
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
 
         final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -170,18 +175,51 @@
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
                         .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+        assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+                        .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
         // Slow downstream
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
                         .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+        assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+                        .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
         // Slow upstream
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
                         .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+        assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+                        .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+        // Medium network is fine for min chunk
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+                        .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+        assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+                        .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+        // Medium downstream
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+                        .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+        assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+                        .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+        // Medium upstream
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+                        .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+        assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+                        .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
         // Fast network looks great
         assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
                         .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+        assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+                createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+                        .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
         // Slow network still good given time
         assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
@@ -241,7 +279,6 @@
         final ConnectivityController controller = new ConnectivityController(mService);
         when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
 
-
         // Suspended networks aren't usable.
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -299,8 +336,17 @@
         final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
         final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
 
+        job.setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, DataUnit.MEBIBYTES.toBytes(1));
+        final JobStatus latePrefetchUnknownDown = createJobStatus(job, now - 2000, now + 1000);
+        job.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), JobInfo.NETWORK_BYTES_UNKNOWN);
+        final JobStatus latePrefetchUnknownUp = createJobStatus(job, now - 2000, now + 1000);
+
         final ConnectivityController controller = new ConnectivityController(mService);
 
+        when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+                any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+                .thenReturn(0L);
+
         // Unmetered network is whenever
         {
             final Network net = mock(Network.class);
@@ -312,9 +358,11 @@
             assertTrue(controller.isSatisfied(late, net, caps, mConstants));
             assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
             assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
         }
 
-        // Metered network is only when prefetching and late
+        // Metered network is only when prefetching, late, and in opportunistic quota
         {
             final Network net = mock(Network.class);
             final NetworkCapabilities caps = createCapabilitiesBuilder()
@@ -323,7 +371,17 @@
             assertFalse(controller.isSatisfied(early, net, caps, mConstants));
             assertFalse(controller.isSatisfied(late, net, caps, mConstants));
             assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+            when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+                    any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+                    .thenReturn(9876543210L);
             assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+            // Only relax restrictions when we at least know the estimated download bytes.
+            assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
         }
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 029930a..906e6d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -3648,7 +3648,7 @@
         // Wait for some extra time to allow for job processing.
         inOrder.verify(mJobSchedulerService,
                 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         synchronized (mQuotaController.mLock) {
             assertEquals(remainingTimeMs / 2,
                     mQuotaController.getRemainingExecutionTimeLocked(jobBg));
@@ -3660,7 +3660,7 @@
         advanceElapsedClock(remainingTimeMs / 2 + 1);
         inOrder.verify(mJobSchedulerService,
                 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         // Top job should still be allowed to run.
         assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3674,7 +3674,7 @@
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         trackJobs(jobFg, jobTop);
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobTop);
@@ -3693,7 +3693,7 @@
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
         inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         // App is now in background and out of quota. Fg should now change to out of quota since it
         // wasn't started. Top should remain in quota since it started when the app was in TOP.
         assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3731,7 +3731,7 @@
         // Wait for some extra time to allow for job processing.
         verify(mJobSchedulerService,
                 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
                 jobStatus.getWhenStandbyDeferred());
@@ -3775,7 +3775,7 @@
         // Wait for some extra time to allow for job processing.
         verify(mJobSchedulerService,
                 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         // The job used up the remaining quota, but in that time, the same amount of time in the
         // old TimingSession also fell out of the quota window, so it should still have the same
@@ -3797,7 +3797,7 @@
         // Wait for some extra time to allow for job processing.
         verify(mJobSchedulerService,
                 timeout(12 * SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         verify(handler, never()).sendMessageDelayed(any(), anyInt());
     }
@@ -5404,7 +5404,7 @@
         // Wait for some extra time to allow for job processing.
         inOrder.verify(mJobSchedulerService,
                 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         synchronized (mQuotaController.mLock) {
             assertEquals(remainingTimeMs / 2,
                     mQuotaController.getRemainingEJExecutionTimeLocked(
@@ -5415,7 +5415,7 @@
         advanceElapsedClock(remainingTimeMs / 2 + 1);
         inOrder.verify(mJobSchedulerService,
                 timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         // Top should still be "in quota" since it started before the app ran on top out of quota.
         assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
         assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
@@ -5440,7 +5440,7 @@
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota.
         inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         trackJobs(jobTop2, jobFg);
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobTop2);
@@ -5461,7 +5461,7 @@
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
         setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
         inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         // App is now in background and out of quota. Fg should now change to out of quota since it
         // wasn't started. Top should remain in quota since it started when the app was in TOP.
         assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
@@ -5601,7 +5601,7 @@
         // Wait for some extra time to allow for job processing.
         verify(mJobSchedulerService,
                 timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
-                .onControllerStateChanged();
+                .onControllerStateChanged(any());
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
         // The job used up the remaining quota, but in that time, the same amount of time in the
         // old TimingSession also fell out of the quota window, so it should still have the same
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index db0c3ae..3c97c95 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -21,13 +21,14 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.FallbackCategoryProvider
 import android.content.pm.FeatureInfo
-import android.content.pm.PackageParser.SigningDetails
 import android.content.pm.ResolveInfo
 import android.content.pm.ServiceInfo
 import android.content.pm.Signature
+import android.content.pm.SigningDetails
 import android.content.pm.UserInfo
 import android.content.pm.parsing.ParsingPackage
 import android.content.pm.parsing.ParsingPackageUtils
+import android.content.pm.parsing.result.ParseTypeImpl
 import android.content.res.Resources
 import android.hardware.display.DisplayManager
 import android.os.Build
@@ -390,8 +391,10 @@
         val apkPath = File(File(parent, packageName), "base.apk")
         val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl
         pkg.signingDetails = signingDetails
-        wheneverStatic { ParsingPackageUtils.getSigningDetails(eq(pkg), anyBoolean()) }
-                .thenReturn(signingDetails)
+        val result = ParseTypeImpl.forDefaultParsing().success(signingDetails)
+        wheneverStatic { ParsingPackageUtils.getSigningDetails(
+                any(ParseTypeImpl::class.java), eq(pkg), anyBoolean()) }
+                .thenReturn(result)
         pkg.versionCode = versionCode.toInt()
         pkg.versionCodeMajor = (versionCode shr 32).toInt()
         pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
@@ -498,8 +501,10 @@
         val apk = File(File(rootDirectory, "framework"), "framework-res.apk")
         val frameworkPkg = PackageImpl.forTesting("android",
                 apk.parentFile.path) as PackageImpl
-        wheneverStatic { ParsingPackageUtils.getSigningDetails(frameworkPkg, true) }
-                .thenReturn(frameworkSignature)
+        val result = ParseTypeImpl.forDefaultParsing().success(frameworkSignature)
+        wheneverStatic { ParsingPackageUtils.getSigningDetails(
+                any(ParseTypeImpl::class.java), eq(frameworkPkg), eq(true)) }
+                .thenReturn(result)
         stageParse(apk, frameworkPkg)
         stageSettingInsert("android",
                 PackageSettingBuilder().setCodePath(apk.path).setName(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index bd44c36..dbbf73b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -18,7 +18,6 @@
 import android.content.pm.ApplicationInfo.FLAG_SYSTEM
 import android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
 import android.content.pm.PackageManager
-import android.content.pm.PackageParser
 import android.os.Build
 import android.os.Process
 import android.util.Log
@@ -122,7 +121,7 @@
                 argThat { path: File -> path.path.contains("a.data.package") },
                 anyInt(),
                 anyBoolean()))
-                .thenThrow(PackageParser.PackageParserException(
+                .thenThrow(PackageManagerException(
                         PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
         val pm = createPackageManagerService()
         verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 521be70..ba5a58f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -19,9 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -123,9 +125,15 @@
         mStagingManager.createSession(session2);
         // Session1 should not fail in spite of the overlapping packages
         mStagingManager.checkNonOverlappingWithStagedSessions(session1);
-        // Session2 should fail due to overlapping packages
+        // setSessionFailed() should've been called when doing overlapping checks on session1
+        verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
+
+        // Yet another session with overlapping packages
+        StagingManager.StagedSession session3 = createSession(333, "com.foo", 3);
+        mStagingManager.createSession(session3);
         assertThrows(PackageManagerException.class,
-                () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
+                () -> mStagingManager.checkNonOverlappingWithStagedSessions(session3));
+        verify(session3, never()).setSessionFailed(anyInt(), anyString());
     }
 
     @Test
@@ -227,8 +235,8 @@
         assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
 
         mStagingManager.onBootCompletedBroadcastReceived();
-        assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
-        assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+        assertThat(nonReadyApkSession.hasVerificationStarted()).isTrue();
+        assertThat(nonReadyApexSession.hasVerificationStarted()).isTrue();
     }
 
     @Test
@@ -565,6 +573,7 @@
             Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
             return filter.test(stagedSession);
         }).when(stagedSession).sessionContains(any());
+        doNothing().when(stagedSession).setSessionFailed(anyInt(), anyString());
         return stagedSession;
     }
 
@@ -581,7 +590,7 @@
         private int mParentSessionId = -1;
         private String mPackageName;
         private boolean mIsAbandonded = false;
-        private boolean mPreRebootVerificationStarted = false;
+        private boolean mVerificationStarted = false;
         private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
 
         private FakeStagedSession(int sessionId) {
@@ -612,8 +621,8 @@
             return mIsAbandonded;
         }
 
-        private boolean hasPreRebootVerificationStarted() {
-            return mPreRebootVerificationStarted;
+        private boolean hasVerificationStarted() {
+            return mVerificationStarted;
         }
 
         private FakeStagedSession addChildSession(FakeStagedSession session) {
@@ -768,20 +777,13 @@
         }
 
         @Override
-        public boolean notifyStartPreRebootVerification() {
-            mPreRebootVerificationStarted = true;
-            // TODO(ioffe): change to true when tests for pre-reboot verification are added.
-            return false;
-        }
-
-        @Override
         public void notifyEndPreRebootVerification() {
             throw new UnsupportedOperationException();
         }
 
         @Override
         public void verifySession() {
-            throw new UnsupportedOperationException();
+            mVerificationStarted = true;
         }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
index 64b24c1..60a7f78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -95,18 +95,34 @@
                     String name = invocationOnMock.getArgument(1);
                     String value = invocationOnMock.getArgument(2);
                     mKeyValueMap.put(getKey(namespace, name), value);
-                    for (DeviceConfig.OnPropertiesChangedListener listener :
-                            mOnPropertiesChangedListenerMap.keySet()) {
-                        if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
-                            mOnPropertiesChangedListenerMap.get(listener).second.execute(
-                                    () -> listener.onPropertiesChanged(
-                                            getProperties(namespace, name, value)));
-                        }
-                    }
+                    invokeListeners(namespace, getProperties(namespace, name, value));
                     return true;
                 }
         ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
 
+        doAnswer((Answer<Boolean>) invocationOnMock -> {
+                    String namespace = invocationOnMock.getArgument(0);
+                    String name = invocationOnMock.getArgument(1);
+                    mKeyValueMap.remove(getKey(namespace, name));
+                    invokeListeners(namespace, getProperties(namespace, name, null));
+                    return true;
+                }
+        ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
+
+        doAnswer((Answer<Boolean>) invocationOnMock -> {
+                    Properties properties = invocationOnMock.getArgument(0);
+                    String namespace = properties.getNamespace();
+                    Map<String, String> keyValues = new ArrayMap<>();
+                    for (String name : properties.getKeyset()) {
+                        String value = properties.getString(name, /* defaultValue= */ "");
+                        mKeyValueMap.put(getKey(namespace, name), value);
+                        keyValues.put(name.toLowerCase(), value);
+                    }
+                    invokeListeners(namespace, getProperties(namespace, keyValues));
+                    return true;
+                }
+        ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
+
         doAnswer((Answer<String>) invocationOnMock -> {
             String namespace = invocationOnMock.getArgument(0);
             String name = invocationOnMock.getArgument(1);
@@ -153,6 +169,16 @@
         return Pair.create(values[0], values[1]);
     }
 
+    private void invokeListeners(String namespace, Properties properties) {
+        for (DeviceConfig.OnPropertiesChangedListener listener :
+                mOnPropertiesChangedListenerMap.keySet()) {
+            if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
+                mOnPropertiesChangedListenerMap.get(listener).second.execute(
+                        () -> listener.onPropertiesChanged(properties));
+            }
+        }
+    }
+
     private Properties getProperties(String namespace, String name, String value) {
         return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
index 0e40669..f9f4387 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -23,6 +23,7 @@
 import android.app.ActivityThread;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.BadConfigException;
 import android.provider.DeviceConfig.Properties;
 
 import androidx.test.filters.SmallTest;
@@ -92,6 +93,30 @@
     }
 
     @Test
+    public void setProperties() throws BadConfigException {
+        String newKey = "key2";
+        String newValue = "value2";
+        DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+                sValue).setString(newKey, newValue).build());
+        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+        assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
+    }
+
+    @Test
+    public void deleteProperty() {
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+        DeviceConfig.deleteProperty(sNamespace, sKey);
+        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
+        String newNamespace = "namespace2";
+        String newValue = "value2";
+        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+        assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
+        DeviceConfig.deleteProperty(newNamespace, sKey);
+        assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
+    }
+
+    @Test
     public void getProperties_empty() {
         String newKey = "key2";
         String newValue = "value2";
@@ -131,13 +156,12 @@
     }
 
     @Test
-    public void testListener() throws InterruptedException {
+    public void testListener_setProperty() throws InterruptedException {
         CountDownLatch countDownLatch = new CountDownLatch(1);
 
         OnPropertiesChangedListener changeListener = (properties) -> {
             assertThat(properties.getNamespace()).isEqualTo(sNamespace);
-            assertThat(properties.getKeyset().size()).isEqualTo(1);
-            assertThat(properties.getKeyset()).contains(sKey);
+            assertThat(properties.getKeyset()).containsExactly(sKey);
             assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
             assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
             countDownLatch.countDown();
@@ -153,6 +177,53 @@
         }
     }
 
+    @Test
+    public void testListener_setProperties() throws BadConfigException, InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        String newKey = "key2";
+        String newValue = "value2";
+
+        OnPropertiesChangedListener changeListener = (properties) -> {
+            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+            assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
+            assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
+            assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
+            assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+            countDownLatch.countDown();
+        };
+        try {
+            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
+            DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+                    sValue).setString(newKey, newValue).build());
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+        } finally {
+            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+        }
+    }
+
+    @Test
+    public void testListener_deleteProperty() throws InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        OnPropertiesChangedListener changeListener = (properties) -> {
+            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+            assertThat(properties.getKeyset()).containsExactly(sKey);
+            assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
+            assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+            countDownLatch.countDown();
+        };
+        try {
+            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
+            DeviceConfig.deleteProperty(sNamespace, sKey);
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+        } finally {
+            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+        }
+    }
 }
 
 
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 339a5f9..d1afa1a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -39,6 +39,7 @@
         "services.usage",
         "services.uwb",
         "guava",
+        "guava-android-testlib",
         "androidx.test.core",
         "androidx.test.ext.truth",
         "androidx.test.runner",
@@ -117,6 +118,7 @@
         ":PackageParserTestApp2",
         ":PackageParserTestApp3",
         ":PackageParserTestApp4",
+        ":PackageParserTestApp5",
         ":apex.test",
         ":test.rebootless_apex_v1",
         ":test.rebootless_apex_v2",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d6c11a5..e612d12 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -65,6 +65,7 @@
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 80e81d6..554f0a4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -29,6 +29,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -176,7 +177,7 @@
     }
 
     @Test
-    public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
+    public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
         prepareLooper();
 
         // Check if there is only one mEventHandler when there is one default display.
@@ -184,13 +185,51 @@
         assertEquals(1, mEventHandler.size());
 
         // Check if it has correct numbers of mEventHandler for corresponding displays.
-        setDisplayCount(4);
-        mA11yInputFilter.onDisplayChanged();
-        assertEquals(4, mEventHandler.size());
+        setDisplayCount(2);
+        mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+        assertEquals(2, mEventHandler.size());
+
+        EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
+        assertNotNull(next);
+
+        // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
+        // DEFAULT_DISPLAY.
+        for (int i = 1; next != null; i++) {
+            assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+            next = next.getNext();
+        }
+    }
+
+    @Test
+    public void testEventHandler_shouldDecreaseAfterOnDisplayRemoved() {
+        prepareLooper();
 
         setDisplayCount(2);
-        mA11yInputFilter.onDisplayChanged();
+        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
         assertEquals(2, mEventHandler.size());
+
+        // Check if it has correct numbers of mEventHandler for corresponding displays.
+        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+        assertEquals(1, mEventHandler.size());
+
+        EventStreamTransformation eventHandler = mEventHandler.get(SECOND_DISPLAY);
+        assertNull(eventHandler);
+    }
+
+    @Test
+    public void testEventHandler_shouldNoChangedInOtherDisplayAfterOnDisplayRemoved() {
+        prepareLooper();
+
+        setDisplayCount(2);
+        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+        EventStreamTransformation eventHandlerBeforeDisplayRemoved =
+                mEventHandler.get(DEFAULT_DISPLAY);
+
+        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+        EventStreamTransformation eventHandlerAfterDisplayRemoved =
+                mEventHandler.get(DEFAULT_DISPLAY);
+
+        assertEquals(eventHandlerBeforeDisplayRemoved, eventHandlerAfterDisplayRemoved);
     }
 
     @Test
@@ -240,7 +279,7 @@
     }
 
     @Test
-    public void testInputEvent_shouldClearEventsForAllEventHandlers() {
+    public void testInputEvent_shouldClearEventsForDisplayEventHandlers() {
         prepareLooper();
 
         mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
@@ -253,13 +292,71 @@
         send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
         assertEquals(2, mCaptor1.mEvents.size());
 
-        // InputEvent with different input source should trigger clearEvents() for each
-        // EventStreamTransformation in EventHandler.
+        // InputEvent with different input source to the same display should trigger
+        // clearEvents() for the EventHandler in this display.
         send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
         assertEquals(1, mCaptor1.mEvents.size());
     }
 
     @Test
+    public void testInputEvent_shouldNotClearEventsForOtherDisplayEventHandlers() {
+        prepareLooper();
+
+        setDisplayCount(2);
+        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+        assertEquals(2, mEventHandler.size());
+
+        mCaptor1 = new EventCaptor();
+        mCaptor2 = new EventCaptor();
+        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+        mEventHandler.put(SECOND_DISPLAY, mCaptor2);
+
+        // InputEvent with different displayId should be dispatched to corresponding EventHandler.
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+
+        // InputEvent with different input source should not trigger clearEvents() for
+        // the EventHandler in the other display.
+        send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_MOUSE));
+        assertEquals(2, mCaptor1.mEvents.size());
+    }
+
+    @Test
+    public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayAdded() {
+        prepareLooper();
+
+        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+        mCaptor1 = new EventCaptor();
+        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        assertEquals(2, mCaptor1.mEvents.size());
+
+        setDisplayCount(2);
+        mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+        assertEquals(2, mCaptor1.mEvents.size());
+    }
+
+    @Test
+    public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayRemoved() {
+        prepareLooper();
+
+        setDisplayCount(2);
+        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+        mCaptor1 = new EventCaptor();
+        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+        assertEquals(2, mCaptor1.mEvents.size());
+
+        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+        assertEquals(2, mCaptor1.mEvents.size());
+    }
+
+    @Test
     public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationGestureHandler() {
         prepareLooper();
         doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index ee00cb2..1a3e53e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -261,8 +261,8 @@
     @Test
     public void resolveValidReportedPackage_uidAndPkgNameMatched_returnPkgName()
             throws PackageManager.NameNotFoundException {
-        when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(APP_UID);
+        when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME,
+                PackageManager.MATCH_ANY_USER, TEST_USER_ID)).thenReturn(APP_UID);
 
         assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
                 PACKAGE_NAME, APP_UID, TEST_USER_ID, APP_PID),
@@ -595,7 +595,7 @@
         serviceInfo.permission = android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE;
         mA11ySecurityPolicy.canRegisterService(serviceInfo);
         verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
-                serviceInfo.applicationInfo.uid, serviceInfo.packageName);
+                serviceInfo.applicationInfo.uid, serviceInfo.packageName, null, null);
     }
 
     @Test
@@ -617,7 +617,7 @@
 
         mA11ySecurityPolicy.checkAccessibilityAccess(mMockA11yServiceConnection);
         verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
-                APP_UID, PACKAGE_NAME);
+                APP_UID, PACKAGE_NAME, null, null);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 00daa5c..432a500 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index e4d51e4..ca9ab4f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -30,7 +30,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -120,6 +119,7 @@
     @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
     @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
     @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+    @Mock private AccessibilityTraceManager mMockA11yTraceManager;
 
     @Mock private IBinder mMockHostToken;
     @Mock private IBinder mMockEmbeddedToken;
@@ -140,7 +140,8 @@
                 mMockWindowManagerInternal,
                 mMockA11yEventSender,
                 mMockA11ySecurityPolicy,
-                mMockA11yUserManager);
+                mMockA11yUserManager,
+                mMockA11yTraceManager);
         // Starts tracking window of default display and sets the default display
         // as top focused display before each testing starts.
         startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
@@ -863,8 +864,6 @@
             windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
         }
         // Turns on windows tracking, and update window info.
-        when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(displayId), any()))
-                .thenReturn(true);
         mA11yWindowManager.startTrackingWindows(displayId);
         // Puts window lists into array.
         mWindowInfos.put(displayId, windowInfosForDisplay);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 78e651b..c62cae5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -56,11 +56,13 @@
     private MessageCapturingHandler mHandler = new MessageCapturingHandler(
             msg -> mInterceptor.handleMessage(msg));
     @Mock AccessibilityManagerService mMockAms;
+    @Mock AccessibilityTraceManager mMockTraceManager;
     @Mock WindowManagerPolicy mMockPolicy;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
         mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index d4908ee..59b69f9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -122,6 +122,7 @@
 
     MotionEventInjector mMotionEventInjector;
     IAccessibilityServiceClient mServiceInterface;
+    AccessibilityTraceManager mTrace;
     List<GestureStep> mLineList = new ArrayList<>();
     List<GestureStep> mClickList = new ArrayList<>();
     List<GestureStep> mContinuedLineList1 = new ArrayList<>();
@@ -148,7 +149,8 @@
                 return mMotionEventInjector.handleMessage(msg);
             }
         });
-        mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler);
+        mTrace = mock(AccessibilityTraceManager.class);
+        mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace);
         mServiceInterface = mock(IAccessibilityServiceClient.class);
 
         mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 1603087..4ce9ba0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.app.UiAutomation;
 import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 7bf0bb8..4a06611f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -36,6 +36,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
 import android.accessibilityservice.AccessibilityService;
@@ -53,6 +54,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.utils.GestureLogParser;
 import com.android.server.testutils.OffsettableClock;
@@ -107,6 +109,8 @@
 
     @Mock
     private AccessibilityManagerService mMockAms;
+    @Mock
+    private AccessibilityTraceManager mMockTraceManager;
     @Captor
     private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor;
 
@@ -143,9 +147,9 @@
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
+        when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
         mContext = InstrumentationRegistry.getContext();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
         mCaptor = new EventCaptor();
         mHandler = new TestHandler();
         mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
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 502f64a..fe4fed9 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
@@ -52,6 +52,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -93,6 +94,7 @@
             mock(FullScreenMagnificationController.ControllerContext.class);
     final Context mMockContext = mock(Context.class);
     final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+    final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
     private final MagnificationAnimationCallback mAnimationCallback = mock(
             MagnificationAnimationCallback.class);
@@ -113,9 +115,11 @@
         when(mMockContext.getMainLooper()).thenReturn(looper);
         when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
         when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+        when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
         when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
         when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
         when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+        when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
         initMockWindowManager();
 
         mFullScreenMagnificationController = new FullScreenMagnificationController(
@@ -773,20 +777,20 @@
     }
 
     @Test
-    public void testRotation_resetsMagnification() {
+    public void testDisplaySizeChanged_resetsMagnification() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
-            rotation_resetsMagnification(i);
+            changeDisplaySize_resetsMagnification(i);
             resetMockWindowManager();
         }
     }
 
-    private void rotation_resetsMagnification(int displayId) {
+    private void changeDisplaySize_resetsMagnification(int displayId) {
         register(displayId);
         MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
         assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
-        callbacks.onRotationChanged(0);
+        callbacks.onDisplaySizeChanged();
         mMessageCapturingHandler.sendAllMessages();
         assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
     }
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 f881f04..b14c353 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
@@ -52,6 +52,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
 import com.android.server.testutils.OffsettableClock;
@@ -129,6 +130,10 @@
     MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
     @Mock
     WindowMagnificationPromptController mWindowMagnificationPromptController;
+    @Mock
+    AccessibilityManagerService mMockAccessibilityManagerService;
+    @Mock
+    AccessibilityTraceManager mMockTraceManager;
 
     private OffsettableClock mClock;
     private FullScreenMagnificationGestureHandler mMgh;
@@ -144,7 +149,9 @@
                 mock(FullScreenMagnificationController.ControllerContext.class);
         final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
         when(mockController.getContext()).thenReturn(mContext);
-        when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+        when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
+        when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
+        when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
         when(mockController.getWindowManager()).thenReturn(mockWindowManager);
         when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
         when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
@@ -179,7 +186,7 @@
     private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
             boolean detectShortcutTrigger) {
         FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
-                mContext, mFullScreenMagnificationController, mMockCallback,
+                mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
                 detectTripleTap, detectShortcutTrigger,
                 mWindowMagnificationPromptController, DISPLAY_0);
         mHandler = new TestHandler(h.mDetectingState, mClock) {
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 b7f5f4d..e82adc8 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
@@ -54,6 +54,7 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -84,6 +85,8 @@
             Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 
     @Mock
+    private AccessibilityTraceManager mTraceManager;
+    @Mock
     private AccessibilityManagerService mService;
     @Mock
     private MagnificationController.TransitionCallBack mTransitionCallBack;
@@ -112,7 +115,7 @@
                 CURRENT_USER_ID);
         mWindowMagnificationManager = Mockito.spy(
                 new WindowMagnificationManager(mContext, CURRENT_USER_ID,
-                        mock(WindowMagnificationManager.Callback.class)));
+                        mock(WindowMagnificationManager.Callback.class), mTraceManager));
         mMockConnection = new MockWindowMagnificationConnection(true);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 514d16a..ef6ed88 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -31,6 +31,8 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,6 +51,8 @@
             Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 
     @Mock
+    AccessibilityTraceManager mTraceManager;
+    @Mock
     MagnificationGestureHandler.Callback mCallback;
 
     @Before
@@ -57,6 +61,7 @@
         mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
                 /* detectTripleTap= */true,
                 /* detectShortcutTrigger= */true,
+                mTraceManager,
                 mCallback);
     }
 
@@ -129,8 +134,9 @@
         boolean mIsInternalMethodCalled = false;
 
         TestMagnificationGestureHandler(int displayId, boolean detectTripleTap,
-                boolean detectShortcutTrigger, @NonNull Callback callback) {
-            super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+                boolean detectShortcutTrigger, @NonNull AccessibilityTraceManager trace,
+                @NonNull Callback callback) {
+            super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index c88bc3b..1638563 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -29,6 +29,8 @@
 import android.view.accessibility.IWindowMagnificationConnectionCallback;
 import android.view.accessibility.MagnificationAnimationCallback;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -45,6 +47,8 @@
 
     private IWindowMagnificationConnection mConnection;
     @Mock
+    private AccessibilityTraceManager mTrace;
+    @Mock
     private IWindowMagnificationConnectionCallback mCallback;
     @Mock
     private MagnificationAnimationCallback mAnimationCallback;
@@ -57,7 +61,7 @@
         MockitoAnnotations.initMocks(this);
         mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
         mConnection = mMockWindowMagnificationConnection.getConnection();
-        mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+        mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b9498d6..6a5aae6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -35,6 +35,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.utils.TouchEventGenerator;
 
@@ -74,16 +75,18 @@
     private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
     @Mock
     MagnificationGestureHandler.Callback mMockCallback;
+    @Mock
+    AccessibilityTraceManager mMockTrace;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
-                mock(WindowMagnificationManager.Callback.class));
+                mock(WindowMagnificationManager.Callback.class), mMockTrace);
         mMockConnection = new MockWindowMagnificationConnection();
         mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
-                mContext, mWindowMagnificationManager, mMockCallback,
+                mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
                 /** detectTripleTap= */true,   /** detectShortcutTrigger= */true, DISPLAY_0);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a20272a..af6d40f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -52,6 +52,7 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import org.junit.Before;
@@ -72,6 +73,8 @@
     @Mock
     private Context mContext;
     @Mock
+    private AccessibilityTraceManager mMockTrace;
+    @Mock
     private StatusBarManagerInternal mMockStatusBarManagerInternal;
     @Mock
     private MagnificationAnimationCallback mAnimationCallback;
@@ -88,7 +91,7 @@
         mResolver = new MockContentResolver();
         mMockConnection = new MockWindowMagnificationConnection();
         mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
-                mMockCallback);
+                mMockCallback, mMockTrace);
 
         when(mContext.getContentResolver()).thenReturn(mResolver);
         doAnswer((InvocationOnMock invocation) -> {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 54945fb..c0ba3c7 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -75,7 +75,7 @@
         for (boolean muted : new boolean[] { true, false}) {
             testAudioSystem.configureIsMicrophoneMuted(!muted);
             mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
-                    UserHandle.getCallingUserId());
+                    UserHandle.getCallingUserId(), null);
             Assert.assertEquals("mic mute reporting wrong value",
                     muted, mAudioService.isMicrophoneMuted());
             // verify the intent for mic mute changed is supposed to be fired
@@ -100,7 +100,7 @@
         for (boolean muted : new boolean[] { true, false}) {
             testAudioSystem.configureIsMicrophoneMuted(!muted);
             mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
-                    UserHandle.getCallingUserId());
+                    UserHandle.getCallingUserId(), null);
             Assert.assertEquals("mic mute reporting wrong value",
                     !muted, mAudioService.isMicrophoneMuted());
             // verify the intent for mic mute changed is supposed to be fired
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 7323096..7aea65e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -20,7 +20,6 @@
 
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.backup.BackupManager.OperationType;
@@ -30,8 +29,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.os.Process;
 import android.os.UserHandle;
@@ -41,7 +40,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.backup.UserBackupManagerService;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -540,9 +538,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -558,9 +556,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -635,9 +633,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -656,9 +654,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -677,9 +675,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {signature1Copy, signature2Copy},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -698,9 +696,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -719,9 +717,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -743,9 +741,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         new Signature[] {SIGNATURE_1, SIGNATURE_2}));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -770,9 +768,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         new Signature[] {SIGNATURE_1, SIGNATURE_2}));
         packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5fcce67..e2536f8 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -39,8 +39,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.os.Bundle;
 import android.os.Process;
@@ -376,9 +376,9 @@
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
@@ -413,9 +413,9 @@
         packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
         packageInfo.applicationInfo.backupAgentName = "backup.agent";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
@@ -451,9 +451,9 @@
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
@@ -492,9 +492,9 @@
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.versionCode = 2;
@@ -536,9 +536,9 @@
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.versionCode = 1;
@@ -576,9 +576,9 @@
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {FAKE_SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.versionCode = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index a19b387..363c26b 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -53,9 +52,7 @@
 import com.android.server.twilight.TwilightState;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
@@ -76,25 +73,29 @@
     private int mUserId;
 
     private MockTwilightManager mTwilightManager;
+    private DisplayTransformManager mDisplayTransformManager;
 
     private ColorDisplayService mCds;
     private ColorDisplayService.BinderService mBinderService;
 
     private Resources mResourcesSpy;
 
-    @BeforeClass
-    public static void setDtm() {
-        final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
-        LocalServices.addService(DisplayTransformManager.class, dtm);
-    }
+    private static final int[] MINIMAL_COLOR_MODES = new int[] {
+        ColorDisplayManager.COLOR_MODE_NATURAL,
+        ColorDisplayManager.COLOR_MODE_BOOSTED,
+    };
 
     @Before
     public void setUp() {
         mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
         doReturn(mContext).when(mContext).getApplicationContext();
 
-        mResourcesSpy = Mockito.spy(mContext.getResources());
-        when(mContext.getResources()).thenReturn(mResourcesSpy);
+        final Resources res = Mockito.spy(mContext.getResources());
+        doReturn(MINIMAL_COLOR_MODES).when(res).getIntArray(R.array.config_availableColorModes);
+        doReturn(true).when(res).getBoolean(R.bool.config_nightDisplayAvailable);
+        doReturn(true).when(res).getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+        when(mContext.getResources()).thenReturn(res);
+        mResourcesSpy = res;
 
         mUserId = ActivityManager.getCurrentUser();
 
@@ -108,6 +109,10 @@
         mTwilightManager = new MockTwilightManager();
         LocalServices.addService(TwilightManager.class, mTwilightManager);
 
+        mDisplayTransformManager = Mockito.mock(DisplayTransformManager.class);
+        doReturn(true).when(mDisplayTransformManager).needsLinearColorMatrix();
+        LocalServices.addService(DisplayTransformManager.class, mDisplayTransformManager);
+
         mCds = new ColorDisplayService(mContext);
         mBinderService = mCds.new BinderService();
         LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -116,12 +121,18 @@
 
     @After
     public void tearDown() {
-        LocalServices.removeServiceForTest(TwilightManager.class);
-
+        /*
+         * Wait for internal {@link Handler} to finish processing pending messages, so that test
+         * code can safelyremove {@link DisplayTransformManager} mock from {@link LocalServices}.
+         */
+        mCds.mHandler.runWithScissors(() -> { /* nop */ }, /* timeout */ 1000);
         mCds = null;
 
+        LocalServices.removeServiceForTest(TwilightManager.class);
         mTwilightManager = null;
 
+        LocalServices.removeServiceForTest(DisplayTransformManager.class);
+
         mUserId = UserHandle.USER_NULL;
         mContext = null;
 
@@ -130,11 +141,6 @@
         LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
-    @AfterClass
-    public static void removeDtm() {
-        LocalServices.removeServiceForTest(DisplayTransformManager.class);
-    }
-
     @Test
     public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
         setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
@@ -1064,24 +1070,18 @@
 
     @Test
     public void compositionColorSpaces_noResources() {
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-        reset(dtm);
-
         when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
             .thenReturn(new int[] {});
         when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
             .thenReturn(new int[] {});
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
-        verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
-                eq(Display.COLOR_MODE_INVALID));
+        verify(mDisplayTransformManager).setColorMode(
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
     }
 
     @Test
     public void compositionColorSpaces_invalidResources() {
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-        reset(dtm);
-
         when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
             .thenReturn(new int[] {
                ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -1094,15 +1094,12 @@
             });
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
-        verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
-                eq(Display.COLOR_MODE_INVALID));
+        verify(mDisplayTransformManager).setColorMode(
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
     }
 
     @Test
     public void compositionColorSpaces_validResources_validColorMode() {
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-        reset(dtm);
-
         when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
             .thenReturn(new int[] {
                ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1113,15 +1110,12 @@
             });
         setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
         startService();
-        verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
-                eq(Display.COLOR_MODE_SRGB));
+        verify(mDisplayTransformManager).setColorMode(
+                eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
     }
 
     @Test
     public void compositionColorSpaces_validResources_invalidColorMode() {
-        final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
-        reset(dtm);
-
         when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
             .thenReturn(new int[] {
                ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1132,8 +1126,8 @@
             });
         setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
         startService();
-        verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(),
-                eq(Display.COLOR_MODE_INVALID));
+        verify(mDisplayTransformManager).setColorMode(
+                eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 6cc8d471..a38a84b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -45,6 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /** Tests for {@link ActiveSourceAction} */
 @SmallTest
@@ -76,7 +77,7 @@
                 mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
-        mHdmiControlService = new HdmiControlService(mContextSpy) {
+        mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
             @Override
             AudioManager getAudioManager() {
                 return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 97bd066..5cf81ec 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -45,6 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /** Tests for {@link ArcInitiationActionFromAvrTest} */
 @SmallTest
@@ -79,7 +80,7 @@
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         HdmiControlService hdmiControlService =
-                new HdmiControlService(mContextSpy) {
+                new HdmiControlService(mContextSpy, Collections.emptyList()) {
                     @Override
                     boolean isPowerStandby() {
                         return false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 29c9b40..1462839 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -45,6 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /** Tests for {@link ArcTerminationActionFromAvr} */
 @SmallTest
@@ -80,7 +81,7 @@
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         HdmiControlService hdmiControlService =
-                new HdmiControlService(mContextSpy) {
+                new HdmiControlService(mContextSpy, Collections.emptyList()) {
                     @Override
                     void wakeUp() {
                     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 8b23be5..f49b1c1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -34,6 +34,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Collections;
+
 /** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
 @SmallTest
 @Presubmit
@@ -53,7 +55,8 @@
     public void SetUp() {
         mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
         HdmiControlService hdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                        Collections.emptyList()) {
 
                     @Override
                     void sendCecCommand(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 650ffe9..fe8d691 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -51,6 +51,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /** Tests for {@link DevicePowerStatusAction} */
 @SmallTest
@@ -88,7 +89,7 @@
                 mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
-        mHdmiControlService = new HdmiControlService(mContextSpy) {
+        mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
             @Override
             AudioManager getAudioManager() {
                 return new AudioManager() {
@@ -220,6 +221,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
         mTestLooper.dispatchAll();
 
@@ -240,6 +242,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
                 .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
         mNativeWrapper.onCecMessage(reportPhysicalAddress);
@@ -263,6 +266,7 @@
         mHdmiControlService.getHdmiCecConfig().setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                 HdmiControlManager.HDMI_CEC_VERSION_2_0);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
                 .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
         mNativeWrapper.onCecMessage(reportPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index fa5cb67..0d5d05c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -54,6 +54,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 @SmallTest
 @RunWith(JUnit4.class)
@@ -105,7 +106,8 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                        Collections.emptyList()) {
                     @Override
                     boolean isControlEnabled() {
                         return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 41946fb..abc1468e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -69,12 +69,27 @@
                 R.bool.config_cecHdmiCecVersion20_default);
 
         doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRoutingControl_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRoutingControlEnabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRoutingControlEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRoutingControlDisabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRoutingControlDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecPowerControlMode_userConfigurable);
         doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecPowerControlModeTv_allowed);
-        doReturn(true).when(resources).getBoolean(
+        doReturn(false).when(resources).getBoolean(
                 R.bool.config_cecPowerControlModeTv_default);
         doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
+        doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecPowerControlModeBroadcast_allowed);
         doReturn(false).when(resources).getBoolean(
                 R.bool.config_cecPowerControlModeBroadcast_default);
@@ -95,6 +110,17 @@
                 R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
 
         doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioControl_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioControlEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioControlEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioControlDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioControlDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecSystemAudioModeMuting_userConfigurable);
         doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 1958cb0..d630ef6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -56,6 +56,7 @@
     private final Map<Integer, Boolean> mPortConnectionStatus = new HashMap<>();
     private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
     private int mMyPhysicalAddress = 0;
+    private int mVendorId = 0;
     private HdmiPortInfo[] mHdmiPortInfo = null;
     private HdmiCecController.HdmiCecCallback mCallback = null;
     private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
@@ -102,7 +103,7 @@
 
     @Override
     public int nativeGetVendorId() {
-        return 0;
+        return mVendorId;
     }
 
     @Override
@@ -181,6 +182,11 @@
     }
 
     @VisibleForTesting
+    protected void setVendorId(int vendorId) {
+        mVendorId = vendorId;
+    }
+
+    @VisibleForTesting
     protected void setPhysicalAddress(int physicalAddress) {
         mMyPhysicalAddress = physicalAddress;
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 29f62b5..da10ec4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -61,6 +62,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /**
  * Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
@@ -103,7 +105,7 @@
                         mIThermalServiceMock, new Handler(mLooper)));
         doReturn(true).when(mIPowerManagerMock).isInteractive();
 
-        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
         doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
@@ -200,7 +202,7 @@
 
         mTestLooper.dispatchAll();
 
-        verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+        verify(mHdmiCecAtomWriterSpy, atLeastOnce()).messageReported(
                 any(),
                 anyInt(),
                 eq(callerUid),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index a37f94b..a94690e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -82,8 +82,10 @@
         assertThat(hdmiCecConfig.getAllSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -103,8 +105,10 @@
         assertThat(hdmiCecConfig.getUserSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -125,7 +129,9 @@
         assertThat(hdmiCecConfig.getUserSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
                     HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -188,6 +194,7 @@
         assertThat(hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                                 HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
                                  HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
                                  HdmiControlManager.POWER_CONTROL_MODE_NONE);
     }
@@ -199,6 +206,7 @@
         assertThat(hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                                 HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
                                  HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
@@ -255,12 +263,12 @@
         HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
-                .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
+                .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
     }
 
     @Test
     public void getDefaultStringValue_WithOverride() {
-        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
         setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
         HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultStringValue(
@@ -277,7 +285,7 @@
 
     @Test
     public void getDefaultStringValue_NoDefault() {
-        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
         assertThrows(RuntimeException.class,
                 () -> new HdmiCecConfig(mContext, mStorageAdapter));
     }
@@ -334,7 +342,7 @@
     public void getStringValue_GlobalSetting_BasicSanity() {
         when(mStorageAdapter.retrieveGlobalSetting(
                   Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
-                  HdmiControlManager.POWER_CONTROL_MODE_TV))
+                  HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM))
             .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
         HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getStringValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index ee1a857..bd6e46d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -69,6 +69,7 @@
 import org.junit.runners.JUnit4;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Optional;
 
 /** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@@ -99,7 +100,7 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlServiceSpy = spy(new HdmiControlService(
-                InstrumentationRegistry.getTargetContext()));
+                InstrumentationRegistry.getTargetContext(), Collections.emptyList()));
         doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
         doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
         doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7911c40..e03e1be 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -53,6 +53,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 @SmallTest
 @Presubmit
@@ -96,7 +97,8 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlService =
-            new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+            new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                    Collections.emptyList()) {
                 @Override
                 AudioManager getAudioManager() {
                     return new AudioManager() {
@@ -210,7 +212,7 @@
         mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
-        mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+        mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
         mHdmiPortInfo = new HdmiPortInfo[4];
         mHdmiPortInfo[0] =
             new HdmiPortInfo(
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 524ad62..99e3d68 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -53,6 +53,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 @SmallTest
@@ -94,7 +95,8 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                        Collections.emptyList()) {
                     @Override
                     void wakeUp() {
                         mWokenUp = true;
@@ -694,20 +696,45 @@
                 HdmiControlManager.POWER_CONTROL_MODE_TV);
         mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
                 "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+    }
+
+    @Test
+    public void handleOnStandby_ScreenOff_NotActiveSource_ToTvAndAudioSystem() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+        mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+                "HdmiCecLocalDevicePlaybackTest");
+        mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
@@ -719,20 +746,20 @@
                 HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
         mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
                 "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
@@ -744,20 +771,20 @@
                 HdmiControlManager.POWER_CONTROL_MODE_NONE);
         mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
                 "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
@@ -769,20 +796,45 @@
                 HdmiControlManager.POWER_CONTROL_MODE_TV);
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+    }
+
+    @Test
+    public void handleOnStandby_ScreenOff_ActiveSource_ToTvAndAudioSystem() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
@@ -794,20 +846,20 @@
                 HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
@@ -819,20 +871,20 @@
                 HdmiControlManager.POWER_CONTROL_MODE_NONE);
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
         HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
                 mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
         assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
     }
@@ -844,9 +896,6 @@
                 HdmiControlManager.POWER_CONTROL_MODE_TV);
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
-        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
-                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
-                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
         mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
 
@@ -1456,7 +1505,7 @@
     }
 
     @Test
-    public void oneTouchPlay_SendStandbyOnSleepToTv() {
+    public void oneTouchPlay_PowerControlModeToTv() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                 HdmiControlManager.POWER_CONTROL_MODE_TV);
@@ -1479,7 +1528,30 @@
     }
 
     @Test
-    public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
+    public void oneTouchPlay_PowerControlModeToTvAndAudioSystem() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+        mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+            }
+        });
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+                ADDR_TV);
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+        HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).contains(systemAudioModeRequest);
+    }
+
+    @Test
+    public void oneTouchPlay_PowerControlModeBroadcast() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                 HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -1502,7 +1574,7 @@
     }
 
     @Test
-    public void oneTouchPlay_SendStandbyOnSleepNone() {
+    public void oneTouchPlay_PowerControlModeNone() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                 HdmiControlManager.POWER_CONTROL_MODE_NONE);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6502e48..6c2db36 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -18,10 +18,8 @@
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
 
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
-import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
-import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
 import static com.android.server.hdmi.Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -40,6 +38,7 @@
 import android.content.Context;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.Result;
 import android.media.AudioManager;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -47,6 +46,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.google.common.testing.EqualsTester;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -141,7 +142,7 @@
         Context context = InstrumentationRegistry.getTargetContext();
 
         mHdmiControlService =
-                new HdmiControlService(context) {
+                new HdmiControlService(context, Collections.emptyList()) {
                     @Override
                     boolean isControlEnabled() {
                         return isControlEnabled;
@@ -205,6 +206,25 @@
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mNativeWrapper.setPhysicalAddress(0x2000);
         mTestLooper.dispatchAll();
+        mNativeWrapper.clearResultMessages();
+    }
+
+    @Test
+    public void testEqualsActiveSource() {
+        int logicalAddress = 0;
+        int physicalAddress = 0x0000;
+        new EqualsTester()
+                .addEqualityGroup(
+                        new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress),
+                        new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress))
+                .addEqualityGroup(
+                        new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress + 1))
+                .addEqualityGroup(
+                        new HdmiCecLocalDevice.ActiveSource(logicalAddress + 1, physicalAddress))
+                .addEqualityGroup(
+                        new HdmiCecLocalDevice.ActiveSource(
+                                logicalAddress + 1, physicalAddress + 1))
+                .testEquals();
     }
 
     @Test
@@ -221,37 +241,71 @@
 
     @Test
     public void handleGivePhysicalAddress_success() {
-        mSrcAddr = ADDR_UNREGISTERED;
-        mDesAddr = ADDR_BROADCAST;
-        param =
-                new byte[] {
-                    (byte) ((mPhysicalAddr >> 8) & 0xFF),
-                    (byte) (mPhysicalAddr & 0xFF),
-                    (byte) (DEVICE_TV & 0xFF)
-                };
-        callbackResult = -1;
-        @Constants.HandleMessageResult int handleResult =
+        mNativeWrapper.setPhysicalAddress(0x0);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_TV, 0, DEVICE_TV);
+        @Constants.HandleMessageResult
+        int handleResult =
                 mHdmiLocalDevice.handleGivePhysicalAddress(
-                        (int finalResult) -> callbackResult = finalResult);
+                        HdmiCecMessageBuilder.buildGivePhysicalAddress(
+                                ADDR_PLAYBACK_1, ADDR_TV));
         mTestLooper.dispatchAll();
-        /**
-         * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
-         */
-        assertEquals(0, callbackResult);
         assertEquals(Constants.HANDLED, handleResult);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void handleGivePhysicalAddress_failure() {
+        mNativeWrapper.setPhysicalAddress(Constants.INVALID_PHYSICAL_ADDRESS);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_TV,
+                        ADDR_PLAYBACK_1,
+                        Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS,
+                        Constants.ABORT_UNABLE_TO_DETERMINE);
+        @Constants.HandleMessageResult
+        int handleResult =
+                mHdmiLocalDevice.handleGivePhysicalAddress(
+                        HdmiCecMessageBuilder.buildGivePhysicalAddress(
+                                ADDR_PLAYBACK_1, ADDR_TV));
+        mTestLooper.dispatchAll();
+        assertEquals(Constants.HANDLED, handleResult);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
     @Test
     public void handleGiveDeviceVendorId_success() {
-        mSrcAddr = ADDR_UNREGISTERED;
-        mDesAddr = ADDR_BROADCAST;
-        /** nativeGetVendorId returns 0 */
-        param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
-        callbackResult = -1;
-        mHdmiLocalDevice.handleGiveDeviceVendorId(
-                (int finalResult) -> callbackResult = finalResult);
+        /** Set vendor id to 0 */
+        mNativeWrapper.setVendorId(0);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildDeviceVendorIdCommand(ADDR_TV, 0);
+        @Constants.HandleMessageResult
+        int handleResult =
+                mHdmiLocalDevice.handleGiveDeviceVendorId(
+                        HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+                                ADDR_PLAYBACK_1, ADDR_TV));
         mTestLooper.dispatchAll();
-        assertEquals(0, callbackResult);
+        assertEquals(Constants.HANDLED, handleResult);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void handleGiveDeviceVendorId_failure() {
+        mNativeWrapper.setVendorId(Result.FAILURE_UNKNOWN);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                        ADDR_TV,
+                        ADDR_PLAYBACK_1,
+                        Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID,
+                        Constants.ABORT_UNABLE_TO_DETERMINE);
+        @Constants.HandleMessageResult
+        int handleResult =
+                mHdmiLocalDevice.handleGiveDeviceVendorId(
+                        HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+                                ADDR_PLAYBACK_1, ADDR_TV));
+        mTestLooper.dispatchAll();
+        assertEquals(Constants.HANDLED, handleResult);
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 59711a6..5a2f7bb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -56,6 +56,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 @SmallTest
 @RunWith(JUnit4.class)
@@ -88,7 +89,8 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                        Collections.emptyList()) {
                     @Override
                     void wakeUp() {
                         mWokenUp = true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
new file mode 100755
index 0000000..036d084
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link HdmiCecMessage} class. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiCecMessageTest {
+
+    @Test
+    public void testEqualsHdmiCecMessage() {
+        int source = 0;
+        int destination = 1;
+        int opcode = 0x7f;
+        byte[] params1 = {0x00, 0x1a, 0x2b, 0x3c};
+        byte[] params2 = {0x00, 0x1a, 0x2b, 0x3c, 0x4d};
+
+        new EqualsTester()
+                .addEqualityGroup(
+                        new HdmiCecMessage(source, destination, opcode, params1),
+                        new HdmiCecMessage(source, destination, opcode, params1))
+                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode, params2))
+                .addEqualityGroup(new HdmiCecMessage(source + 1, destination, opcode, params1))
+                .addEqualityGroup(new HdmiCecMessage(source, destination + 1, opcode, params1))
+                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode + 1, params1))
+                .testEquals();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 2307a85..ebd8d77 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -37,6 +37,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Collections;
+
 /** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */
 @SmallTest
 @Presubmit
@@ -49,7 +51,7 @@
     @Before
     public void setUp() throws Exception {
         HdmiControlService mHdmiControlService = new HdmiControlService(
-                InstrumentationRegistry.getTargetContext());
+                InstrumentationRegistry.getTargetContext(), Collections.emptyList());
 
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
         mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index b1998f5..e7d1b95 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -64,7 +64,7 @@
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
-        mHdmiControlService = new HdmiControlService(mContext) {
+        mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList()) {
             @Override
             void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
                 mDeviceEventListenerStatuses.add(status);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 572ffd9..dc633a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -47,6 +47,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 @SmallTest
 @Presubmit
@@ -83,7 +84,7 @@
                 mIThermalServiceMock, new Handler(myLooper)));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
-        mHdmiControlService = new HdmiControlService(contextSpy) {
+        mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList()) {
             @Override
             boolean isControlEnabled() {
                 return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index f53ae52..755eef3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -63,6 +63,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Optional;
 
 /**
@@ -205,7 +206,7 @@
 
         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
 
-        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
         doNothing().when(mHdmiControlServiceSpy)
                 .writeStringSystemProperty(anyString(), anyString());
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index 9f0cdd5..c89c32a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -31,6 +31,8 @@
 import com.android.server.hdmi.HdmiUtils.CodecSad;
 import com.android.server.hdmi.HdmiUtils.DeviceConfig;
 
+import com.google.common.testing.EqualsTester;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -116,6 +118,51 @@
     }
 
     @Test
+    public void testEqualsCodecSad() {
+        byte[] sad = {0x0a, 0x1b, 0x2c};
+        String sadString = "0a1b2c";
+        new EqualsTester()
+                .addEqualityGroup(
+                        new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sad),
+                        new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString))
+                .addEqualityGroup(
+                        new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString + "01"))
+                .addEqualityGroup(new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString))
+                .addEqualityGroup(
+                        new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString + "01"))
+                .testEquals();
+    }
+
+    @Test
+    public void testEqualsDeviceConfig() {
+        String name = "Name";
+
+        CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03");
+        CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506");
+        CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203");
+        CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506");
+
+        List<CodecSad> list1 = new ArrayList();
+        list1.add(expectedCodec1);
+        list1.add(expectedCodec2);
+        list1.add(expectedCodec3);
+
+        List<CodecSad> list1Duplicate = new ArrayList(list1);
+
+        List<CodecSad> list2 = new ArrayList(list1);
+        list2.add(expectedCodec4);
+
+        new EqualsTester()
+                .addEqualityGroup(
+                        new HdmiUtils.DeviceConfig(name, list1),
+                        new HdmiUtils.DeviceConfig(name, list1Duplicate))
+                .addEqualityGroup(new HdmiUtils.DeviceConfig(name, list2))
+                .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list1))
+                .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list2))
+                .testEquals();
+    }
+
+    @Test
     public void parseSampleXML() {
         List<DeviceConfig> config = new ArrayList<>();
         try {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index b820df3..9466f52 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,8 +91,6 @@
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
         mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
 
-        setHdmiControlEnabled(hdmiControlEnabled);
-
         when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
                 new PowerManager(mContextSpy, mIPowerManagerMock,
                 mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
@@ -137,6 +135,7 @@
         Looper looper = mTestLooper.getLooper();
         mHdmiControlService.setIoLooper(looper);
         mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
+        setHdmiControlEnabled(hdmiControlEnabled);
         mNativeWrapper = new FakeNativeWrapper();
         HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
new file mode 100644
index 0000000..bd307fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+import static com.android.server.hdmi.Constants.MESSAGE_ACTIVE_SOURCE;
+import static com.android.server.hdmi.Constants.MESSAGE_ROUTING_INFORMATION;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTING_INFORMATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RoutingControlActionTest {
+    /*
+     * Example connection diagram used in tests. Double-lined paths indicate the currently active
+     * routes.
+     *
+     *
+     *                              +-----------+
+     *                              |    TV     |
+     *                              |  0.0.0.0  |
+     *                              +---+-----+-+
+     *                                  |     |
+     *                               <----------+ 1) AVR -> Switch
+     *             +----------+         |     |  +-----------+
+     *             | AVR      +---------+     +--+ Switch    |
+     *             | 1.0.0.0  |                  | 2.0.0.0   |
+     *             +--+---++--+                  +--++-----+-+  <-------+ 2) Recorder -> Blu-ray
+     *                |   ||                        ||     |
+     *                |   ||                        ||     +--------+
+     * +-----------+  |   ||  +----------+     +----++----+         |
+     * | XBox      +--+   ++--+ Tuner    |     | Blueray  |   +-----+----+
+     * | 1.1.0.0   |          | 1.2.0.0  |     | 2.1.0.0  |   | Recorder |
+     * +-----------+          +----++----+     +----------+   | 2.2.0.0  |
+     *                             ||                         +----------+
+     *                             ||
+     *                        +----++----+
+     *                        | Player   |
+     *                        | 1.2.1.0  |
+     *                        +----------+
+     *
+     */
+
+    private static final int PHYSICAL_ADDRESS_TV = 0x0000;
+    private static final int PHYSICAL_ADDRESS_AVR = 0x1000;
+    private static final int PHYSICAL_ADDRESS_SWITCH = 0x2000;
+    private static final int PHYSICAL_ADDRESS_TUNER = 0x1200;
+    private static final int PHYSICAL_ADDRESS_PLAYER = 0x1210;
+    private static final int PHYSICAL_ADDRESS_BLUERAY = 0x2100;
+    private static final int PHYSICAL_ADDRESS_RECORDER = 0x2200;
+    private static final int PORT_1 = 1;
+    private static final int PORT_2 = 2;
+    private static final int VENDOR_ID_AVR = 0x11233;
+
+    private static final byte[] TUNER_PARAM =
+            new byte[] {(PHYSICAL_ADDRESS_TUNER >> 8) & 0xFF, PHYSICAL_ADDRESS_TUNER & 0xFF};
+    private static final byte[] PLAYER_PARAM =
+            new byte[] {(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
+
+    private static final HdmiDeviceInfo DEVICE_INFO_AVR =
+            new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
+                    HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
+    private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
+            new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
+                    HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
+    private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+            ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
+    private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+            ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
+    private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+            ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
+    private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+            ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
+
+    private HdmiControlService mHdmiControlService;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+    private FakeNativeWrapper mNativeWrapper;
+    private Looper mMyLooper;
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private IThermalService mIThermalServiceMock;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        Context context = InstrumentationRegistry.getTargetContext();
+        mMyLooper = mTestLooper.getLooper();
+        PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mMyLooper));
+
+        HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
+
+        mHdmiControlService =
+                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                        Collections.emptyList()) {
+                    @Override
+                    boolean isControlEnabled() {
+                        return true;
+                    }
+
+                    @Override
+                    void wakeUp() {
+                    }
+
+                    @Override
+                    protected void writeStringSystemProperty(String key, String value) {
+                        // do nothing
+                    }
+
+                    @Override
+                    boolean isPowerStandbyOrTransient() {
+                        return false;
+                    }
+
+                    @Override
+                    protected PowerManager getPowerManager() {
+                        return powerManager;
+                    }
+
+                    @Override
+                    protected HdmiCecConfig getHdmiCecConfig() {
+                        return hdmiCecConfig;
+                    }
+                };
+
+        mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+        mHdmiCecLocalDeviceTv.init();
+        mHdmiControlService.setIoLooper(mMyLooper);
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+        mHdmiControlService.setCecController(mHdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mLocalDevices.add(mHdmiCecLocalDeviceTv);
+        HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+        hdmiPortInfos[0] =
+                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
+                                 true, false, false);
+        mNativeWrapper.setPortInfo(hdmiPortInfos);
+        mHdmiControlService.initService();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mNativeWrapper.setPhysicalAddress(0x0000);
+        mTestLooper.dispatchAll();
+        mNativeWrapper.clearResultMessages();
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+    }
+
+    private static class TestActionTimer implements ActionTimer {
+        private int mState;
+
+        @Override
+        public void sendTimerMessage(int state, long delayMillis) {
+            mState = state;
+        }
+
+        @Override
+        public void clearTimerMessage() {
+        }
+
+        private int getState() {
+            return mState;
+        }
+    }
+
+    private static class TestInputSelectCallback extends IHdmiControlCallback.Stub {
+        private final List<Integer> mCallbackResult = new ArrayList<Integer>();
+
+        @Override
+        public void onComplete(int result) {
+            mCallbackResult.add(result);
+        }
+
+        private int getResult() {
+            assert (mCallbackResult.size() == 1);
+            return mCallbackResult.get(0);
+        }
+    }
+
+    private static RoutingControlAction createRoutingControlAction(HdmiCecLocalDeviceTv localDevice,
+            TestInputSelectCallback callback) {
+        return new RoutingControlAction(localDevice, PHYSICAL_ADDRESS_AVR, callback);
+    }
+
+    // Routing control succeeds against the device connected directly to the port. Action
+    // won't get any <Routing Information> in this case. It times out on <Routing Information>,
+    // regards the directly connected one as the new routing path to switch to.
+    @Test
+    public void testRoutingControl_succeedForDirectlyConnectedDevice() {
+        TestInputSelectCallback callback = new TestInputSelectCallback();
+        TestActionTimer actionTimer = new TestActionTimer();
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+
+        RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+        action.setActionTimer(actionTimer);
+        action.start();
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+        action.handleTimerEvent(actionTimer.getState());
+        mTestLooper.dispatchAll();
+        HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+                        ADDR_TV, PHYSICAL_ADDRESS_AVR);
+        assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+    }
+
+    // Succeeds by receiving a couple of <Routing Information> commands, followed by
+    // <Set Stream Path> going out in the end.
+    @Test
+    public void testRoutingControl_succeedForDeviceBehindSwitch() {
+        TestInputSelectCallback callback = new TestInputSelectCallback();
+        TestActionTimer actionTimer = new TestActionTimer();
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_PLAYER);
+        RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+        action.setActionTimer(actionTimer);
+        action.start();
+
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+        action.processCommand(ROUTING_INFORMATION_TUNER);
+        action.processCommand(ROUTING_INFORMATION_PLAYER);
+
+        action.handleTimerEvent(actionTimer.getState());
+        mTestLooper.dispatchAll();
+        HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+                        ADDR_TV, PHYSICAL_ADDRESS_PLAYER);
+        assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 2cf4ef1..290e4b0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -48,6 +48,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 
 /**
  * Test for {@link SystemAudioAutoInitiationAction}.
@@ -86,7 +87,7 @@
                 mIThermalServiceMock, new Handler(myLooper)));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
-        mHdmiControlService = new HdmiControlService(mContextSpy) {
+        mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
             @Override
             AudioManager getAudioManager() {
                 return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index e82c788..2019a16 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -38,6 +38,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Collections;
+
 /** Tests for {@link SystemAudioInitiationActionFromAvr} */
 @SmallTest
 @Presubmit
@@ -65,7 +67,8 @@
 
         Context context = InstrumentationRegistry.getTargetContext();
 
-        HdmiControlService hdmiControlService = new HdmiControlService(context) {
+        HdmiControlService hdmiControlService = new HdmiControlService(context,
+                Collections.emptyList()) {
                     @Override
                     void sendCecCommand(
                             HdmiCecMessage command, @Nullable SendMessageCallback callback) {
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
deleted file mode 100644
index 9ab762a..0000000
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ /dev/null
@@ -1,92 +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.inputmethod;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MultiClientInputMethodManagerServiceTest {
-
-    @Before
-    public void setUp() {
-        // MultiClientInputMethodManagerService is only testable if build is debuggable.
-        assumeTrue(Build.IS_DEBUGGABLE);
-    }
-
-    @Test
-    public void testQueryInputMethod_noIMEFound() {
-        assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                emptyList())).isNull();
-    }
-
-    @Test
-    public void testQueryInputMethod_multipleIMEsFound() {
-        assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                asList(new ResolveInfo(), new ResolveInfo()))).isNull();
-    }
-
-    @Test
-    public void testQueryInputMethod_IMEFound_invalidPermission() {
-        // Arrange
-        ResolveInfo imeService = buildResolveInfo(/* permission= */ "",
-                ApplicationInfo.FLAG_SYSTEM);
-
-        // Act and assert
-        assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                asList(imeService))).isNull();
-    }
-
-    @Test
-    public void testQueryInputMethod_IMEFound() {
-        // Arrange
-        ResolveInfo imeService = buildResolveInfo(android.Manifest.permission.BIND_INPUT_METHOD,
-                ApplicationInfo.FLAG_SYSTEM);
-
-        // Act and assert
-        assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                asList(imeService))).isSameInstanceAs(imeService);
-    }
-
-    private ResolveInfo buildResolveInfo(String permission, int flags) {
-        ResolveInfo imeService = new ResolveInfo();
-        imeService.serviceInfo = new ServiceInfo();
-        imeService.serviceInfo.packageName = "com.android.server.inputmethod";
-        imeService.serviceInfo.name = "someIMEService";
-        imeService.serviceInfo.permission = permission;
-        imeService.serviceInfo.applicationInfo = new ApplicationInfo();
-        imeService.serviceInfo.applicationInfo.flags = flags;
-        return imeService;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 67dd055..be942d6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,9 +30,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
@@ -252,8 +251,8 @@
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         final Signature frameworkSignature = Mockito.mock(Signature.class);
-        final PackageParser.SigningDetails frameworkSigningDetails =
-                new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+        final SigningDetails frameworkSigningDetails =
+                new SigningDetails(new Signature[]{frameworkSignature}, 1);
         final ParsingPackage android = pkg("android");
         watcher.verifyNoChangeReported("prepare");
         android.addProtectedBroadcast("TEST_ACTION");
@@ -592,12 +591,12 @@
         appsFilter.onSystemReady();
 
         final Signature frameworkSignature = Mockito.mock(Signature.class);
-        final PackageParser.SigningDetails frameworkSigningDetails =
-                new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+        final SigningDetails frameworkSigningDetails =
+                new SigningDetails(new Signature[]{frameworkSignature}, 1);
 
         final Signature otherSignature = Mockito.mock(Signature.class);
-        final PackageParser.SigningDetails otherSigningDetails =
-                new PackageParser.SigningDetails(new Signature[]{otherSignature}, 1);
+        final SigningDetails otherSigningDetails =
+                new SigningDetails(new Signature[]{otherSignature}, 1);
 
         simulateAddPackage(appsFilter, pkg("android"), 1000,
                 b -> b.setSigningDetails(frameworkSigningDetails));
@@ -1199,8 +1198,8 @@
 
     private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception {
         final Signature frameworkSignature = Mockito.mock(Signature.class);
-        final PackageParser.SigningDetails frameworkSigningDetails =
-                new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+        final SigningDetails frameworkSigningDetails =
+                new SigningDetails(new Signature[]{frameworkSignature}, 1);
         final ParsingPackage android = pkg("android");
         simulateAddPackage(appsFilter, android, 1000,
                 b -> b.setSigningDetails(frameworkSigningDetails));
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 c572dd6..4f77afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -77,12 +77,12 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
@@ -1413,9 +1413,9 @@
         pi.applicationInfo.setVersionCode(version);
         pi.signatures = null;
         pi.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         genSignatures(signatures),
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         return pi;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
index 94ac9be..0f1b14b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -30,9 +30,9 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Build;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -49,7 +49,7 @@
 
     @Before
     public void setUp() {
-        mCompatibilityModeEnabled = PackageParser.sCompatibilityModeEnabled;
+        mCompatibilityModeEnabled = ParsingPackageUtils.sCompatibilityModeEnabled;
         mMockAndroidPackage = mock(AndroidPackage.class);
         mMockUserState = mock(PackageUserState.class);
         mMockUserState.installed = true;
@@ -226,9 +226,9 @@
     }
 
     private void setGlobalCompatibilityMode(boolean enabled) {
-        if (PackageParser.sCompatibilityModeEnabled == enabled) {
+        if (ParsingPackageUtils.sCompatibilityModeEnabled == enabled) {
             return;
         }
-        PackageParser.setCompatibilityModeEnabled(enabled);
+        ParsingPackageUtils.setCompatibilityModeEnabled(enabled);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 128cbaa..e8c5fb3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.pm;
 
+import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -25,6 +29,7 @@
 
 import static java.lang.Boolean.TRUE;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.stream.Collectors.toList;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -35,10 +40,10 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -83,6 +88,7 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -106,6 +112,7 @@
     private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
     private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
     private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+    private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
     private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
 
     @Before
@@ -506,6 +513,59 @@
         }
     }
 
+    @Test
+    public void testParseModernPackageHasNoCompatPermissions() throws Exception {
+        final File testFile = extractFile(TEST_APP1_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2()
+                    .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+            final List<String> compatPermissions =
+                    Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                            .collect(toList());
+            assertWithMessage(
+                    "Compatibility permissions shouldn't be added into uses permissions.")
+                    .that(pkg.getUsesPermissions().stream().map(ParsedUsesPermission::getName)
+                            .collect(toList()))
+                    .containsNoneIn(compatPermissions);
+            assertWithMessage(
+                    "Compatibility permissions shouldn't be added into requested permissions.")
+                    .that(pkg.getRequestedPermissions()).containsNoneIn(compatPermissions);
+            assertWithMessage(
+                    "Compatibility permissions shouldn't be added into implicit permissions.")
+                    .that(pkg.getImplicitPermissions()).containsNoneIn(compatPermissions);
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
+    public void testParseLegacyPackageHasCompatPermissions() throws Exception {
+        final File testFile = extractFile(TEST_APP5_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2()
+                    .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+            assertWithMessage(
+                    "Compatibility permissions should be added into uses permissions.")
+                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                            .allMatch(pkg.getUsesPermissions().stream()
+                                    .map(ParsedUsesPermission::getName)
+                            .collect(toList())::contains))
+                    .isTrue();
+            assertWithMessage(
+                    "Compatibility permissions should be added into requested permissions.")
+                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                            .allMatch(pkg.getRequestedPermissions()::contains))
+                    .isTrue();
+            assertWithMessage(
+                    "Compatibility permissions should be added into implicit permissions.")
+                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                            .allMatch(pkg.getImplicitPermissions()::contains))
+                    .isTrue();
+        } finally {
+            testFile.delete();
+        }
+    }
+
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
@@ -645,7 +705,8 @@
         assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
         assertEquals(a.getVersionName(), b.getVersionName());
         assertEquals(a.getSharedUserId(), b.getSharedUserId());
-        assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
+        assertArrayEquals(a.getSigningDetails().getSignatures(),
+                b.getSigningDetails().getSignatures());
         assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
         assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
         assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
@@ -653,7 +714,7 @@
         assertEquals(a.getOverlayCategory(), b.getOverlayCategory());
         assertEquals(a.getOverlayPriority(), b.getOverlayPriority());
         assertEquals(a.isOverlayIsStatic(), b.isOverlayIsStatic());
-        assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
+        assertEquals(a.getSigningDetails().getPublicKeys(), b.getSigningDetails().getPublicKeys());
         assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
         assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
         assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
@@ -873,9 +934,9 @@
                 .setVersionName("foo17")
                 .setSharedUserId("foo18")
                 .setSigningDetails(
-                        new PackageParser.SigningDetails(
+                        new SigningDetails(
                                 new Signature[]{new Signature(new byte[16])},
-                                2,
+                                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
                                 new ArraySet<>(),
                                 null)
                 )
@@ -896,8 +957,8 @@
                 .setVisibleToInstantApps(true)
                 .setSplitHasCode(0, true)
                 .hideAsParsed())
-                .setBaseCodePath("foo5")
-                .setCodePath("foo4")
+                .setBaseApkPath("foo5")
+                .setPath("foo4")
                 .setVersionCode(100)
                 .setRestrictUpdateHash(new byte[16])
                 .setVersionCodeMajor(100)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f75751b..f551ad1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,8 +16,8 @@
 
 package com.android.server.pm;
 
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
+import android.content.pm.SigningDetails;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -47,7 +47,7 @@
     private String[] mUsesStaticLibraries;
     private long[] mUsesStaticLibrariesVersions;
     private Map<String, ArraySet<String>> mMimeGroups;
-    private PackageParser.SigningDetails mSigningDetails;
+    private SigningDetails mSigningDetails;
     private UUID mDomainSetId = UUID.randomUUID();
 
     public PackageSettingBuilder setPackage(AndroidPackage pkg) {
@@ -159,7 +159,7 @@
     }
 
     public PackageSettingBuilder setSigningDetails(
-            PackageParser.SigningDetails signingDetails) {
+            SigningDetails signingDetails) {
         mSigningDetails = signingDetails;
         return this;
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 27f3eec..b9431bf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -22,11 +22,9 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
-import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.util.TypedXmlPullParser;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -39,7 +37,6 @@
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 
-import java.io.File;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -107,10 +104,10 @@
     }
 
     private static final int[] CAPABILITIES =
-            {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
-                    PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
-                    PackageParser.SigningDetails.CertCapabilities.PERMISSION,
-                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+            {SigningDetails.CertCapabilities.INSTALLED_DATA,
+                    SigningDetails.CertCapabilities.SHARED_USER_ID,
+                    SigningDetails.CertCapabilities.PERMISSION,
+                    SigningDetails.CertCapabilities.ROLLBACK};
 
     @Before
     public void setUp() throws Exception {
@@ -173,7 +170,7 @@
         assertEquals(
                 "The signing details was not UNKNOWN after parsing an invalid public key cert key"
                         + " attribute",
-                PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+                SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
     }
 
     @Test
@@ -181,14 +178,14 @@
         // Verifies if the sigs count attribute is missing then the signature cannot be read but the
         // method does not throw an exception.
         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
-                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+                SigningDetails.SignatureSchemeVersion.UNKNOWN);
     }
 
     @Test
     public void testReadXmlWithMissingSchemeVersion() throws Exception {
         // Verifies if the schemeVersion is an invalid value the signature can still be obtained.
         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
-                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+                SigningDetails.SignatureSchemeVersion.UNKNOWN,
                 FIRST_EXPECTED_SIGNATURE);
     }
 
@@ -198,7 +195,7 @@
         // obtained.
         verifyReadXmlReturnsExpectedSignaturesAndLineage(
                 "xml/three-signers-in-lineage-missing-scheme-version.xml",
-                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+                SigningDetails.SignatureSchemeVersion.UNKNOWN,
                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
     }
 
@@ -386,7 +383,7 @@
         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
         assertEquals("The returned signature scheme is not the expected value",
                 expectedSchemeVersion,
-                mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+                mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
     }
 
     /**
@@ -402,7 +399,7 @@
         Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
         assertEquals("The returned signature scheme is not the expected value", schemeVersion,
-                mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+                mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
         for (Signature signature : signatures) {
             String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
             int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index f1930d7..901b200 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,7 @@
 public class SELinuxMMACTest {
 
     private static final String PACKAGE_NAME = "my.package";
-    private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+    private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
     private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
 
     @Mock
@@ -91,6 +91,16 @@
     }
 
     @Test
+    public void getSeInfoTargetingCurDevelopment() {
+        AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                .thenReturn(true);
+        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+                is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
+    }
+
+    @Test
     public void getSeInfoNoOptInButAlreadyR() {
         AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 8e1fc16..8153242 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -51,11 +51,11 @@
 import android.util.Pair;
 
 import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -241,7 +241,7 @@
                 .setPackageName("static.lib.pkg.123")
                 .setVersionCodeMajor(1)
                 .setVersionCode(234)
-                .setBaseCodePath("/some/path.apk")
+                .setBaseApkPath("/some/path.apk")
                 .setSplitCodePaths(new String[] {"/some/other/path.apk"});
 
         final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(pkg)
@@ -273,7 +273,7 @@
                 .hideAsParsed())
                 .setVersionCodeMajor(1)
                 .setVersionCode(234)
-                .setBaseCodePath("/some/path.apk")
+                .setBaseApkPath("/some/path.apk")
                 .setSplitCodePaths(new String[] {"/some/other/path.apk"});
 
         final PackageManagerService.ScanRequest scanRequest =
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 182760b..b447857 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -25,8 +25,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.platform.test.annotations.Presubmit;
 import android.test.MoreAsserts;
@@ -95,9 +95,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -114,9 +114,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -197,9 +197,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -219,9 +219,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -240,9 +240,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -262,9 +262,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -285,9 +285,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -309,9 +309,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -336,9 +336,9 @@
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
         packageInfo.signingInfo = new SigningInfo(
-                new PackageParser.SigningDetails(
+                new SigningDetails(
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
-                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index bc0a540..16f72f7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -23,7 +23,6 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
@@ -137,25 +136,30 @@
     }
 
     private static void validatePackageDexMetadata(AndroidPackage pkg, boolean requireManifest)
-            throws PackageParserException {
+            throws PackageManagerException {
         Collection<String> apkToDexMetadataList =
                 AndroidPackageUtils.getPackageDexMetadata(pkg).values();
         String packageName = pkg.getPackageName();
         long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
         for (String dexMetadata : apkToDexMetadataList) {
-            DexMetadataHelper.validateDexMetadataFile(
-                    dexMetadata, packageName, versionCode, requireManifest);
+            final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
+                    input.reset(), dexMetadata, packageName, versionCode, requireManifest);
+            if (result.isError()) {
+                throw new PackageManagerException(
+                        result.getErrorCode(), result.getErrorMessage(), result.getException());
+            }
         }
     }
 
     private static void validatePackageDexMetatadataVaryingRequireManifest(ParsedPackage pkg)
-            throws PackageParserException {
+            throws PackageManagerException {
         validatePackageDexMetadata(pkg, /*requireManifest=*/true);
         validatePackageDexMetadata(pkg, /*requireManifest=*/false);
     }
 
     @Test
-    public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
+    public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk");
         ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
@@ -172,7 +176,7 @@
 
     @Test
     public void testParsePackageSplitsWithDmFileValid()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_base.apk");
@@ -195,7 +199,7 @@
 
     @Test
     public void testParsePackageSplitsNoBaseWithDmFileValid()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_feature_a.apk");
@@ -221,7 +225,7 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: empty .dm file");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
 
@@ -229,14 +233,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/false);
             fail("Should fail validation: empty .dm file");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageSplitsWithDmFileInvalid()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_base.apk");
@@ -247,7 +251,7 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: empty .dm file");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
 
@@ -255,14 +259,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/false);
             fail("Should fail validation: empty .dm file");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileInvalidManifest()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
 
@@ -270,14 +274,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: missing manifest.json in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileEmptyManifest()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", /*packageName=*/"doesn't matter",
                 /*versionCode=*/-12345L, /*emptyManifest=*/true, /*validManifest=*/true);
@@ -286,14 +290,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: empty manifest.json in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileBadPackageName()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", /*packageName=*/"bad package name",
                 DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -302,14 +306,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: bad package name in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileBadVersionCode()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
                 /*versionCode=*/12345L, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -318,14 +322,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: bad version code in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileMissingPackageName()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", /*packageName=*/null,
                 DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -334,14 +338,14 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: missing package name in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
 
     @Test
     public void testParsePackageWithDmFileMissingVersionCode()
-            throws IOException, PackageParserException {
+            throws IOException, PackageManagerException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
                 /*versionCode=*/null, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -350,7 +354,7 @@
             ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
             validatePackageDexMetadata(pkg, /*requireManifest=*/true);
             fail("Should fail validation: missing version code in the .dm archive");
-        } catch (PackageParserException e) {
+        } catch (PackageManagerException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
         }
     }
@@ -370,7 +374,7 @@
 
     @Test
     public void testPackageSplitsWithDmFileNoMatch()
-            throws IOException, PackageParserException {
+            throws IOException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_base.apk");
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index c2b3858..1502839 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -17,7 +17,6 @@
 package com.android.server.pm.parsing;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -29,12 +28,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
@@ -48,6 +49,7 @@
 
 import com.android.frameworks.servicestests.R;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 
@@ -335,7 +337,7 @@
      * Copies a specified {@code resourceId} to a file. Returns a non-null file if the copy
      * succeeded, or {@code null} otherwise.
      */
-    File copyRawResourceToFile(String baseName, int resourceId) throws Exception {
+    File copyRawResourceToFile(String baseName, int resourceId) {
         // Copy the resource to a file.
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         InputStream is = context.getResources().openRawResource(resourceId);
@@ -537,8 +539,14 @@
             throw new IllegalStateException(result.getErrorMessage(), result.getException());
         }
 
+        ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
         ParsingPackage pkg = result.getResult();
-        pkg.setSigningDetails(ParsingPackageUtils.getSigningDetails(pkg, false));
+        ParseResult<SigningDetails> ret = ParsingPackageUtils.getSigningDetails(
+                input, pkg, false /*skipVerify*/);
+        if (ret.isError()) {
+            throw new IllegalStateException(ret.getErrorMessage(), ret.getException());
+        }
+        pkg.setSigningDetails(ret.getResult());
         PackageInfo pi = PackageInfoWithoutStateUtils.generate(pkg, apexInfo, flags);
 
         assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
@@ -596,7 +604,7 @@
             try {
                 parsePackage(filename, resId, x -> x);
                 expect.withMessage("Expected parsing error %d from %s", result, filename).fail();
-            } catch (PackageParser.PackageParserException expected) {
+            } catch (PackageManagerException expected) {
                 expect.that(expected.error).isEqualTo(result);
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 4cd057c..ffa1957 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,8 +17,9 @@
 package com.android.server.pm.parsing
 
 import android.content.pm.PackageManager
-import android.content.pm.PackageParser
+import android.content.pm.parsing.ParsingPackageUtils
 import android.platform.test.annotations.Postsubmit
+import com.android.server.pm.PackageManagerException
 import com.android.server.pm.PackageManagerService
 import com.android.server.pm.PackageManagerServiceUtils
 import org.junit.Rule
@@ -80,11 +81,12 @@
         val exceptions = buildApks()
                 .map {
                     runCatching {
-                        parser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
+                        parser.parsePackage(
+                                it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false /*useCaches*/)
                     }
                 }
                 .mapNotNull { it.exceptionOrNull() }
-                .filterNot { (it as? PackageParser.PackageParserException)?.error ==
+                .filterNot { (it as? PackageManagerException)?.error ==
                         PackageManager.INSTALL_PARSE_FAILED_SKIPPED }
 
         if (exceptions.isEmpty()) return
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5eabc1b..b431614 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -741,6 +741,33 @@
     }
 
     @Test
+    public void testSuspendBlockerHeldDuringBoot() throws Exception {
+        final String suspendBlockerName = "PowerManagerService.Booting";
+
+        final boolean[] isAcquired = new boolean[1];
+        doAnswer(inv -> {
+            isAcquired[0] = false;
+            return null;
+        }).when(mNativeWrapperMock).nativeReleaseSuspendBlocker(eq(suspendBlockerName));
+
+        doAnswer(inv -> {
+            isAcquired[0] = true;
+            return null;
+        }).when(mNativeWrapperMock).nativeAcquireSuspendBlocker(eq(suspendBlockerName));
+
+        // Need to create the service after we stub the mocks for this test because some of the
+        // mocks are used during the constructor.
+        createService();
+        assertTrue(isAcquired[0]);
+
+        mService.systemReady(null);
+        assertTrue(isAcquired[0]);
+
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        assertFalse(isAcquired[0]);
+    }
+
+    @Test
     public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(120);
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 443476c..aa6ee09 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -290,45 +290,6 @@
         assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
     }
 
-    public void testDeviceSpecific() {
-        mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1;
-        mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, "");
-        mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
-
-        mBatterySaverPolicy.onChange();
-        assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
-        assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
-
-
-        mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_2;
-
-        mBatterySaverPolicy.onChange();
-        assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
-        assertThat(mBatterySaverPolicy.getFileValues(false).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq=123, "
-                        + "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=456}");
-
-        mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
-
-        mBatterySaverPolicy.onChange();
-        assertThat(mBatterySaverPolicy.getFileValues(true).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=333, "
-                        + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=444}");
-        assertThat(mBatterySaverPolicy.getFileValues(false).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=222}");
-
-
-        mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
-                "cpufreq-i=3:1234567890/4:014/5:015");
-
-        mBatterySaverPolicy.onChange();
-        assertThat(mBatterySaverPolicy.getFileValues(true).toString())
-                .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=1234567890, "
-                        + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=14, "
-                        + "/sys/devices/system/cpu/cpu5/cpufreq/scaling_max_freq=15}");
-        assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
-    }
-
     public void testSetPolicyLevel_Off() {
         mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
deleted file mode 100644
index 796394f..0000000
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
+++ /dev/null
@@ -1,87 +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.power.batterysaver;
-
-import static org.junit.Assert.assertEquals;
-
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class CpuFrequenciesTest {
-    private void check(ArrayMap<String, String> expected, String config) {
-        CpuFrequencies actual = new CpuFrequencies().parseString(config);
-        assertEquals(expected, actual.toSysFileMap());
-    }
-
-    @Test
-    public void test() {
-        check(new ArrayMap<>(), "");
-
-        final ArrayMap<String, String> expected = new ArrayMap<>();
-
-        expected.clear();
-        expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "0");
-        check(expected, "0:0");
-
-        expected.clear();
-        expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "0");
-        expected.put("/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq", "1");
-        check(expected, "0:0/1:1");
-
-        expected.clear();
-        expected.put("/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq", "0");
-        expected.put("/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq", "1234567890");
-        check(expected, "2:0/1:1234567890");
-
-        expected.clear();
-        expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "1900800");
-        expected.put("/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq", "1958400");
-        check(expected, "0:1900800/4:1958400");
-
-        check(expected, "0:1900800/4:1958400/"); // Shouldn't crash.
-        check(expected, "0:1900800/4:1958400/1"); // Shouldn't crash.
-        check(expected, "0:1900800/4:1958400/a:1"); // Shouldn't crash.
-        check(expected, "0:1900800/4:1958400/1:"); // Shouldn't crash.
-        check(expected, "0:1900800/4:1958400/1:b"); // Shouldn't crash.
-    }
-
-    @Test
-    public void testToString_returnsSanitizedStringUsedToParse() {
-        String inputString = "0:1900800/4:1958400/a:1";
-        String expectedString = "0:1900800/4:1958400";
-        CpuFrequencies cpuFrequencies = new CpuFrequencies();
-        cpuFrequencies.parseString(inputString);
-        assertEquals(expectedString, cpuFrequencies.toString());
-    }
-
-    @Test
-    public void testEquals_objectsParsedFromSameStringShouldBeEqual() {
-        String inputString = "0:1900800/4:1958400/a:1";
-        CpuFrequencies cpuFrequencies1 = new CpuFrequencies().parseString(inputString);
-        CpuFrequencies cpuFrequencies2 = new CpuFrequencies().parseString(inputString);
-        assertEquals(cpuFrequencies1, cpuFrequencies2);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
deleted file mode 100644
index ecdc58e..0000000
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
+++ /dev/null
@@ -1,405 +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.power.batterysaver;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.platform.test.annotations.FlakyTest;
-import android.util.ArrayMap;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FileUpdaterTest {
-
-    private class FileUpdaterTestable extends FileUpdater {
-        FileUpdaterTestable(Context context, Looper looper, int maxRetries, int retryIntervalMs) {
-            super(context, looper, maxRetries, retryIntervalMs);
-        }
-
-        @Override
-        String injectReadFromFileTrimmed(String file) throws IOException {
-            return mInjector.injectReadFromFileTrimmed(file);
-        }
-
-        @Override
-        void injectWriteToFile(String file, String value) throws IOException {
-            mInjector.injectWriteToFile(file, value);
-        }
-
-        @Override
-        void injectWtf(String message, Throwable e) {
-            mInjector.injectWtf(message, e);
-        }
-
-        @Override
-        File injectDefaultValuesFilename() {
-            return new File(InstrumentationRegistry.getContext().getCacheDir() +
-                    "/test-default.xml");
-        }
-
-        @Override
-        boolean injectShouldSkipWrite() {
-            return false;
-        }
-    }
-
-    private interface Injector {
-        String injectReadFromFileTrimmed(String file) throws IOException;
-        void injectWriteToFile(String file, String value) throws IOException;
-        void injectWtf(String message, Throwable e);
-    }
-
-    private Handler mMainHandler;
-
-    @Mock
-    private Injector mInjector;
-
-    private static final int MAX_RETRIES = 3;
-
-    private FileUpdaterTestable mInstance;
-
-    public static <T> T anyOrNull(Class<T> clazz) {
-        return ArgumentMatchers.argThat(value -> true);
-    }
-
-    public static String anyOrNullString() {
-        return ArgumentMatchers.argThat(value -> true);
-    }
-
-    @Before
-    public void setUp() {
-        mMainHandler = new Handler(Looper.getMainLooper());
-
-        MockitoAnnotations.initMocks(this);
-
-        mInstance = newInstance();
-    }
-
-    private FileUpdaterTestable newInstance() {
-        return new FileUpdaterTestable(
-                InstrumentationRegistry.getContext(),
-                Looper.getMainLooper(),
-                MAX_RETRIES,
-                0 /* retry with no delays*/);
-    }
-
-    private void waitUntilMainHandlerDrain() throws Exception {
-        final CountDownLatch l = new CountDownLatch(1);
-        mMainHandler.post(() -> l.countDown());
-        assertTrue(l.await(5, TimeUnit.SECONDS));
-    }
-
-    private void veriryWtf(int times) {
-        verify(mInjector, times(times)).injectWtf(anyOrNullString(), anyOrNull(Throwable.class));
-    }
-
-    @Test
-    public void testNoWrites() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(0)).injectWriteToFile(anyOrNullString(), anyOrNullString());
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(0)).injectWriteToFile(anyOrNullString(), anyOrNullString());
-
-        // No WTF should have happened.
-        veriryWtf(0);
-    }
-
-    @Test
-    public void testSimpleWrite() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-
-        // No WTF should have happened.
-        veriryWtf(0);
-    }
-
-    @Test
-    public void testMultiWrites() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-        values.put("file2", "22");
-        values.put("file3", "33");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-        verify(mInjector, times(1)).injectWriteToFile("file3", "33");
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-        verify(mInjector, times(1)).injectWriteToFile("file3", "333");
-
-        // No WTF should have happened.
-        veriryWtf(0);
-    }
-
-    @Test
-    public void testCantReadDefault() throws Exception {
-        doThrow(new IOException("can't read")).when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-        values.put("file2", "22");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(0)).injectWriteToFile("file1", "11");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
-        veriryWtf(1);
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(0)).injectWriteToFile("file1", "111");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-
-        veriryWtf(1);
-    }
-
-    @Test
-    public void testWriteGiveUp() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("fail1");
-
-        doThrow(new IOException("can't write")).when(mInjector).injectWriteToFile(
-                eq("fail1"), eq("33"));
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-        values.put("file2", "22");
-        values.put("fail1", "33");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
-        verify(mInjector, times(MAX_RETRIES + 1)).injectWriteToFile("fail1", "33");
-
-        // 1 WTF.
-        veriryWtf(1);
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-
-        verify(mInjector, times(1)).injectWriteToFile("fail1", "333");
-
-        // No further WTF.
-        veriryWtf(1);
-    }
-
-    @Test
-    public void testSuccessWithRetry() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("fail1");
-
-        final AtomicInteger counter = new AtomicInteger();
-        doAnswer((inv) -> {
-            if (counter.getAndIncrement() <= 1) {
-                throw new IOException();
-            }
-            return null;
-            }).when(mInjector).injectWriteToFile(eq("fail1"), eq("33"));
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-        values.put("file2", "22");
-        values.put("fail1", "33");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
-        // Should succeed after 2 retries.
-        verify(mInjector, times(3)).injectWriteToFile("fail1", "33");
-
-        // No WTF.
-        veriryWtf(0);
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-        verify(mInjector, times(1)).injectWriteToFile("fail1", "333");
-
-        // Still no WTF.
-        veriryWtf(0);
-    }
-
-    @FlakyTest
-    @Test
-    public void testAll() throws Exception {
-        // Run multiple tests on the single target instance.
-
-        reset(mInjector);
-        testSimpleWrite();
-
-        reset(mInjector);
-        testWriteGiveUp();
-
-        reset(mInjector);
-        testMultiWrites();
-
-        reset(mInjector);
-        testSuccessWithRetry();
-
-        reset(mInjector);
-        testMultiWrites();
-    }
-
-    @Test
-    public void testWriteReadDefault() throws Exception {
-        doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
-        doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-        doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
-        // Write
-        final ArrayMap<String, String> values = new ArrayMap<>();
-        values.put("file1", "11");
-        values.put("file2", "22");
-        values.put("file3", "33");
-
-        mInstance.writeFiles(values);
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-        verify(mInjector, times(1)).injectWriteToFile("file3", "33");
-
-        // Clear and reload the default.
-        assertEquals(3, mInstance.getDefaultValuesForTest().size());
-        mInstance.getDefaultValuesForTest().clear();
-        assertEquals(0, mInstance.getDefaultValuesForTest().size());
-
-        mInstance.systemReady(/*runtimeRestarted=*/ true);
-
-        assertEquals(3, mInstance.getDefaultValuesForTest().size());
-
-        // Reset to default
-        mInstance.restoreDefault();
-        waitUntilMainHandlerDrain();
-
-        verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-        verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-        verify(mInjector, times(1)).injectWriteToFile("file3", "333");
-
-        // Make sure the default file still exists.
-        assertTrue(mInstance.injectDefaultValuesFilename().exists());
-
-        // Simulate a clean boot.
-        mInstance.getDefaultValuesForTest().clear();
-        assertEquals(0, mInstance.getDefaultValuesForTest().size());
-
-        mInstance.systemReady(/*runtimeRestarted=*/ false);
-
-        // Default is empty, and the file is gone.
-        assertEquals(0, mInstance.getDefaultValuesForTest().size());
-        assertFalse(mInstance.injectDefaultValuesFilename().exists());
-
-        // No WTF should have happened.
-        veriryWtf(0);
-   }
-}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 4618157..2a4c3fd 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -32,6 +32,7 @@
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
+import android.util.SparseIntArray;
 
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.Installer;
@@ -119,8 +120,9 @@
     }
 
     private static Rollback createRollbackForId(int rollbackId) {
-        return new Rollback(rollbackId, new File("/does/not/exist"), -1,
-                0, "com.xyz");
+        return new Rollback(rollbackId, new File("/does/not/exist"), -1, /* isStaged */ false, 0,
+                "com.xyz", null, new SparseIntArray(0));
+
     }
 
     @Test
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 c42f936..9d56a36 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -82,7 +82,7 @@
             + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
             + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
             + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
-            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+            + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
             + "'restoreUserDataInProgress':true, 'userId':0,"
             + "'installerPackageName':'some.installer'}";
 
@@ -102,7 +102,7 @@
             + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
             + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
             + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
-            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+            + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
             + "'restoreUserDataInProgress':true, 'userId':0,"
             + "'installerPackageName':'some.installer',"
             + "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
@@ -129,12 +129,13 @@
         SparseIntArray extensionVersions = new SparseIntArray();
         extensionVersions.put(30, 71);
         Rollback rollback = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, extensionVersions);
+                ID, 567, USER, INSTALLER, null, extensionVersions);
 
         assertThat(rollback.getBackupDir().getAbsolutePath())
                 .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
 
         assertThat(rollback.isStaged()).isFalse();
+        assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
         assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
         assertThat(rollback.info.getPackages()).isEmpty();
         assertThat(rollback.isEnabling()).isTrue();
@@ -153,7 +154,7 @@
                 .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
 
         assertThat(rollback.isStaged()).isTrue();
-        assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+        assertThat(rollback.getOriginalSessionId()).isEqualTo(897);
 
         assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
         assertThat(rollback.info.getPackages()).isEmpty();
@@ -168,7 +169,7 @@
         extensionVersions.put(5, 25);
         extensionVersions.put(30, 71);
         Rollback origRb = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, extensionVersions);
+                ID, 567, USER, INSTALLER, null, extensionVersions);
 
         origRb.setRestoreUserDataInProgress(true);
         origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -218,7 +219,7 @@
     @Test
     public void loadFromJsonNoExtensionVersions() throws Exception {
         Rollback expectedRb = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, new SparseIntArray(0));
+                ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
 
         expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
         expectedRb.setRestoreUserDataInProgress(true);
@@ -268,7 +269,7 @@
         extensionVersions.put(5, 25);
         extensionVersions.put(30, 71);
         Rollback expectedRb = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, extensionVersions);
+                ID, 567, USER, INSTALLER, null, extensionVersions);
 
         expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
         expectedRb.setRestoreUserDataInProgress(true);
@@ -315,7 +316,7 @@
     @Test
     public void saveAndDelete() {
         Rollback rollback = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, new SparseIntArray(0));
+                ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
 
         RollbackStore.saveRollback(rollback);
 
@@ -331,7 +332,7 @@
     @Test
     public void saveToHistoryAndLoad() {
         Rollback origRb = mRollbackStore.createNonStagedRollback(
-                ID, USER, INSTALLER, null, new SparseIntArray(0));
+                ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
         mRollbackStore.saveRollbackToHistory(origRb);
 
         List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks();
@@ -364,7 +365,7 @@
         assertThat(b.getApexPackageNames())
                 .containsExactlyElementsIn(a.getApexPackageNames());
 
-        assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+        assertThat(b.getOriginalSessionId()).isEqualTo(a.getOriginalSessionId());
 
         assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index cf1ed48..5ba4851 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -74,18 +74,28 @@
         when(mMockPmi.getPackageList()).thenReturn(mPackageList);
     }
 
+    private Rollback createStagedRollback(int rollbackId, File backupDir, int originalSessionId) {
+        return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, USER,
+                INSTALLER, null, new SparseIntArray(0));
+    }
+
+    private Rollback createNonStagedRollback(int rollbackId, File backupDir) {
+        return new Rollback(rollbackId, backupDir, -1, /* isStaged */ false, USER,
+                INSTALLER, null, new SparseIntArray(0));
+    }
+
     @Test
     public void newEmptyStagedRollbackDefaults() {
         int rollbackId = 123;
         int sessionId = 567;
         File file = new File("/test/testing");
 
-        Rollback rollback = new Rollback(rollbackId, file, sessionId, USER, INSTALLER);
+        Rollback rollback = createStagedRollback(rollbackId, file, sessionId);
 
         assertThat(rollback.isEnabling()).isTrue();
         assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
         assertThat(rollback.isStaged()).isTrue();
-        assertThat(rollback.getStagedSessionId()).isEqualTo(567);
+        assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
     }
 
     @Test
@@ -93,7 +103,7 @@
         int rollbackId = 123;
         File file = new File("/test/testing");
 
-        Rollback rollback = new Rollback(rollbackId, file, -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(rollbackId, file);
 
         assertThat(rollback.isEnabling()).isTrue();
         assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -102,8 +112,7 @@
 
     @Test
     public void rollbackMadeAvailable() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER,
-                INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
 
         assertThat(rollback.isEnabling()).isTrue();
         assertThat(rollback.isAvailable()).isFalse();
@@ -121,7 +130,7 @@
 
     @Test
     public void deletedRollbackCannotBeMadeAvailable() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
 
         rollback.delete(mMockDataHelper, "test");
 
@@ -135,7 +144,7 @@
 
     @Test
     public void getPackageNamesAllAndJustApex() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
         PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
@@ -149,7 +158,7 @@
 
     @Test
     public void includesPackagesAfterEnable() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
@@ -177,7 +186,7 @@
 
     @Test
     public void snapshotWhenEnabling() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -195,7 +204,7 @@
 
     @Test
     public void snapshotWhenAvailable() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -216,7 +225,7 @@
 
     @Test
     public void snapshotWhenDeleted() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -237,7 +246,7 @@
 
     @Test
     public void snapshotThenDeleteNoApex() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, false);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -259,7 +268,7 @@
 
     @Test
     public void snapshotThenDeleteWithApex() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -282,7 +291,7 @@
 
     @Test
     public void restoreUserDataDoesNothingIfNotInProgress() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -297,7 +306,7 @@
 
     @Test
     public void restoreUserDataDoesNothingIfPackageNotFound() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -313,7 +322,7 @@
 
     @Test
     public void restoreUserDataRestoresIfInProgressAndPackageFound() {
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
         PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -329,20 +338,9 @@
     }
 
     @Test
-    public void notifySessionWithSuccess() {
-        int[] sessionIds = new int[]{ 7777, 8888 };
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
-                sessionIds, new SparseIntArray(0));
-        // The 1st invocation returns false because not all child sessions are notified.
-        assertThat(rollback.notifySessionWithSuccess()).isFalse();
-        // The 2nd invocation returns true because now all child sessions are notified.
-        assertThat(rollback.notifySessionWithSuccess()).isTrue();
-    }
-
-    @Test
     public void allPackagesEnabled() {
         int[] sessionIds = new int[]{ 7777, 8888 };
-        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, false, USER, INSTALLER,
                 sessionIds, new SparseIntArray(0));
         // #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
         rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
index fa2123c..03ccf8c 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -76,7 +76,8 @@
         // setup a spy for the RotationResolverManagerPerUserService.
         final RotationResolverManagerService mainService = new RotationResolverManagerService(
                 mContext);
-        mService = new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
+        final Object lock = new Object();
+        mService = new RotationResolverManagerPerUserService(mainService, lock,
                 mContext.getUserId());
 
         mCancellationSignal = new CancellationSignal();
@@ -84,15 +85,13 @@
         mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
                 true, 1000L);
         this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
-                mMockCallbackInternal, mRequest, mCancellationSignal);
+                mMockCallbackInternal, mRequest, mCancellationSignal, lock);
 
         this.mService.getMaster().mIsServiceEnabled = true;
 
         ComponentName componentName = new ComponentName(PACKAGE_NAME, CLASS_NAME);
         this.mService.mRemoteService = new MockRemoteRotationResolverService(mContext,
-                componentName, mContext.getUserId(),
-                /* idleUnbindTimeoutMs */60000L,
-                /* Lock */ new Object());
+                componentName, mContext.getUserId(), /* idleUnbindTimeoutMs */60000L);
     }
 
     @Test
@@ -126,13 +125,13 @@
     }
 
     static class MockRemoteRotationResolverService extends RemoteRotationResolverService {
-        MockRemoteRotationResolverService(Context context, ComponentName serviceName,
-                int userId, long idleUnbindTimeoutMs, Object lock) {
-            super(context, serviceName, userId, idleUnbindTimeoutMs, lock);
+        MockRemoteRotationResolverService(Context context, ComponentName serviceName, int userId,
+                long idleUnbindTimeoutMs) {
+            super(context, serviceName, userId, idleUnbindTimeoutMs);
         }
 
         @Override
-        public void resolveRotationLocked(RotationRequest request) {
+        public void resolveRotation(RotationRequest request) {
             request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
new file mode 100644
index 0000000..1947481
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.Status;
+import android.os.HwParcel;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+import android.os.RemoteException;
+import android.system.OsConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class SoundHw2CompatTest {
+    @Parameterized.Parameter(0) public String mVersion;
+    @Parameterized.Parameter(1) public boolean mSupportConcurrentCapture;
+
+    private final Runnable mRebootRunnable = mock(Runnable.class);
+    private ISoundTriggerHal mCanonical;
+    private CaptureStateNotifier mCaptureStateNotifier;
+    private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+
+    // We run the test once for every version of the underlying driver.
+    @Parameterized.Parameters(name = "{0}, concurrent={1}")
+    public static Iterable<Object[]> data() {
+        List<Object[]> result = new LinkedList<>();
+
+        for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3", "V2_4",}) {
+            for (boolean concurrentCapture : new boolean[]{false, true}) {
+                result.add(new Object[]{version, concurrentCapture});
+            }
+        }
+
+        return result;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mHalDriver = (android.hardware.soundtrigger.V2_0.ISoundTriggerHw) mock(Class.forName(
+                String.format("android.hardware.soundtrigger.%s.ISoundTriggerHw", mVersion)));
+
+        clearInvocations(mRebootRunnable);
+
+        // This binder is associated with the mock, so it can be cast to either version of the
+        // HAL interface.
+        final IHwBinder binder = new IHwBinder() {
+            @Override
+            public void transact(int code, HwParcel request, HwParcel reply, int flags)
+                    throws RemoteException {
+                // This is a little hacky, but a very easy way to gracefully reject a request for
+                // an unsupported interface (after queryLocalInterface() returns null, the client
+                // will attempt a remote transaction to obtain the interface. RemoteException will
+                // cause it to give up).
+                throw new RemoteException();
+            }
+
+            @Override
+            public IHwInterface queryLocalInterface(String descriptor) {
+                if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
+                        || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
+                        || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
+                        || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw
+                        || descriptor.equals("android.hardware.soundtrigger@2.4::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+                    return mHalDriver;
+                }
+                return null;
+            }
+
+            @Override
+            public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+                try {
+                    return mHalDriver.linkToDeath(recipient, cookie);
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                }
+            }
+
+            @Override
+            public boolean unlinkToDeath(DeathRecipient recipient) {
+                try {
+                    return mHalDriver.unlinkToDeath(recipient);
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                }
+            }
+        };
+        when(mHalDriver.asBinder()).thenReturn(binder);
+
+        android.hardware.soundtrigger.V2_3.Properties halProperties =
+                TestUtil.createDefaultProperties_2_3(mSupportConcurrentCapture);
+        doAnswer(invocation -> {
+            ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
+                    0)).onValues(0, halProperties.base);
+            return null;
+        }).when(mHalDriver).getProperties(any());
+
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                    (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+            doAnswer(invocation -> {
+                ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback) invocation.getArgument(
+                        0)).onValues(0, halProperties);
+                return null;
+            }).when(driver).getProperties_2_3(any());
+        }
+
+        mCaptureStateNotifier = spy(new CaptureStateNotifier());
+
+        mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable,
+                mCaptureStateNotifier);
+
+        // During initialization any method can be called, but after we're starting to enforce that
+        // no additional methods are called.
+        clearInvocations(mHalDriver);
+    }
+
+    @After
+    public void tearDown() {
+        mCanonical.detach();
+        verifyNoMoreInteractions(mHalDriver);
+        verifyNoMoreInteractions(mRebootRunnable);
+        mCaptureStateNotifier.verifyNoMoreListeners();
+    }
+
+    @Test
+    public void testSetUpAndTearDown() {
+    }
+
+    @Test
+    public void testReboot() {
+        mCanonical.reboot();
+        verify(mRebootRunnable).run();
+    }
+
+    @Test
+    public void testGetProperties() throws Exception {
+        Properties properties = mCanonical.getProperties();
+
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                    (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+            // It is OK for the SUT to cache the properties, so the underlying method doesn't
+            // need to be called every single time.
+            verify(driver, atMost(1)).getProperties_2_3(any());
+            TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture);
+        } else {
+            // It is OK for the SUT to cache the properties, so the underlying method doesn't
+            // need to be called every single time.
+            verify(mHalDriver, atMost(1)).getProperties(any());
+            TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture, 0, "");
+        }
+    }
+
+    private int loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
+
+        assertEquals(handle,
+                mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+        verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), anyInt(),
+                any());
+
+        TestUtil.validateGenericSoundModel_2_0(modelCaptor.getValue());
+        validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    private int loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(driver_2_1).loadSoundModel_2_1(any(), any(), anyInt(), any());
+
+        assertEquals(handle,
+                mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+        verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+                anyInt(), any());
+
+        TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+        validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    private int loadGenericModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+                    resultCallback = invocation.getArgument(2);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+        assertEquals(handle,
+                mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+        verify(driver_2_4).loadSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+                any());
+
+        TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+        validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            return loadGenericModel_2_4(canonicalCallback);
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            return loadGenericModel_2_1(canonicalCallback);
+        } else {
+            return loadGenericModel_2_0(canonicalCallback);
+        }
+    }
+
+    @Test
+    public void testLoadGenericModel() throws Exception {
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        loadGenericModel(canonicalCallback);
+    }
+
+    @Test
+    public void testMaxModels() throws Exception {
+        assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw);
+
+        // Register global callback.
+        ISoundTriggerHal.GlobalCallback globalCallback = mock(
+                ISoundTriggerHal.GlobalCallback.class);
+        mCanonical.registerCallback(globalCallback);
+
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        final int maxModels = TestUtil.createDefaultProperties_2_0(false).maxSoundModels;
+        int[] modelHandles = new int[maxModels];
+
+        // Load as many models as we're allowed.
+        for (int i = 0; i < maxModels; ++i) {
+            modelHandles[i] = loadGenericModel(canonicalCallback);
+            verifyNoMoreInteractions(mHalDriver);
+            clearInvocations(mHalDriver);
+        }
+
+        // Now try to load an additional one and expect failure without invoking the underlying
+        // driver.
+        try {
+            mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+
+        // Unload a single model and expect a onResourcesAvailable().
+        mCanonical.unloadSoundModel(modelHandles[0]);
+        verify(mHalDriver).unloadSoundModel(modelHandles[0]);
+
+        mCanonical.flushCallbacks();
+        verify(globalCallback).onResourcesAvailable();
+    }
+
+    private void testLoadGenericModelBusy_2_4() throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+                    resultCallback = invocation.getArgument(2);
+
+            // This is the return of this method.
+            resultCallback.onValues(-OsConstants.EBUSY, 0);
+            return null;
+        }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        try {
+            mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+        verify(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+    }
+
+    @Test
+    public void testLoadGenericModelBusy() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            testLoadGenericModelBusy_2_4();
+        }
+    }
+
+    private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
+                modelCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+
+        assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+                canonicalCallback));
+
+        verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+                anyInt(), any());
+
+        TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue());
+        validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    private int loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+                modelCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(driver_2_1).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+
+        assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+                canonicalCallback));
+
+        verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+                anyInt(), any());
+
+        TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+        validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    private int loadPhraseModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        final int handle = 29;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+                modelCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+                    resultCallback = invocation.getArgument(2);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, handle);
+            return null;
+        }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+        assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+                canonicalCallback));
+
+        verify(driver_2_4).loadPhraseSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+                any());
+
+        TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+        validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+        return handle;
+    }
+
+    public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            return loadPhraseModel_2_4(canonicalCallback);
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            return loadPhraseModel_2_1(canonicalCallback);
+        } else {
+            return loadPhraseModel_2_0(canonicalCallback);
+        }
+    }
+
+    @Test
+    public void testLoadPhraseModel() throws Exception {
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        loadPhraseModel(canonicalCallback);
+    }
+
+    private void testLoadPhraseModelBusy_2_4() throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+                    resultCallback = invocation.getArgument(2);
+
+            // This is the return of this method.
+            resultCallback.onValues(-OsConstants.EBUSY, 0);
+            return null;
+        }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        try {
+            mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+        verify(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+    }
+
+    @Test
+    public void testLoadPhraseModelBusy() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            testLoadPhraseModelBusy_2_4();
+        }
+    }
+
+    @Test
+    public void testUnloadModel() throws Exception {
+        mCanonical.unloadSoundModel(14);
+        verify(mHalDriver).unloadSoundModel(14);
+    }
+
+    private void startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
+                configCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+        when(mHalDriver.startRecognition(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        mCanonical.startRecognition(handle, 203, 204, config);
+        verify(mHalDriver).startRecognition(eq(handle), configCaptor.capture(),
+                callbackCaptor.capture(), anyInt());
+
+        TestUtil.validateRecognitionConfig_2_0(configCaptor.getValue(), 203, 204);
+        validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+    }
+
+    private void startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
+                configCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+        when(driver_2_1.startRecognition_2_1(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        mCanonical.startRecognition(handle, 505, 506, config);
+        verify(driver_2_1).startRecognition_2_1(eq(handle), configCaptor.capture(),
+                callbackCaptor.capture(), anyInt());
+
+        TestUtil.validateRecognitionConfig_2_1(configCaptor.getValue(), 505, 506);
+        validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+    }
+
+    private void startRecognition_2_3(int handle) throws Exception {
+        final android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+                ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+        when(driver_2_3.startRecognition_2_3(eq(handle), any())).thenReturn(0);
+
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        mCanonical.startRecognition(handle, 808, 909, config);
+        verify(driver_2_3).startRecognition_2_3(eq(handle), configCaptor.capture());
+        TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
+    }
+
+    private void startRecognition_2_4(int handle) throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+        ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+                ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+        when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(0);
+
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        mCanonical.startRecognition(handle, 21, 22, config);
+        verify(driver_2_4).startRecognition_2_4(eq(handle), configCaptor.capture());
+        TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 21, 22);
+    }
+
+    private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+            throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            startRecognition_2_4(handle);
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            startRecognition_2_3(handle);
+        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            startRecognition_2_1(handle, canonicalCallback);
+        } else {
+            startRecognition_2_0(handle, canonicalCallback);
+        }
+    }
+
+    @Test
+    public void testStartRecognition() throws Exception {
+        // First load.
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        final int handle = loadGenericModel(canonicalCallback);
+
+        // Then start.
+        startRecognition(handle, canonicalCallback);
+    }
+
+    private void testStartRecognitionBusy_2_4() throws Exception {
+        final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        final int handle = 68;
+        when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(-OsConstants.EBUSY);
+
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        try {
+            mCanonical.startRecognition(handle, 34, 35, config);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+        verify(driver_2_4).startRecognition_2_4(eq(handle), any());
+    }
+
+    @Test
+    public void testStartRecognitionBusy() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            testStartRecognitionBusy_2_4();
+        }
+    }
+
+    @Test
+    public void testNoRegisterCaptureStateListener() {
+        assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+                || mSupportConcurrentCapture);
+        verify(mCaptureStateNotifier, never()).registerListener(any());
+    }
+
+    @Test
+    public void testConcurrentCaptureAbort() throws Exception {
+        assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+                || mSupportConcurrentCapture);
+        verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+        // Register global callback.
+        ISoundTriggerHal.GlobalCallback globalCallback = mock(
+                ISoundTriggerHal.GlobalCallback.class);
+        mCanonical.registerCallback(globalCallback);
+
+        // Load.
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        final int handle = loadGenericModel(canonicalCallback);
+
+        // Then start.
+        startRecognition(handle, canonicalCallback);
+
+        // Now activate external capture.
+        mCaptureStateNotifier.setState(true);
+
+        // Expect hardware to have been stopped.
+        verify(mHalDriver).stopRecognition(handle);
+
+        // Expect an abort event (async).
+        ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                RecognitionEvent.class);
+        mCanonical.flushCallbacks();
+        verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+
+        // Deactivate external capture.
+        mCaptureStateNotifier.setState(false);
+
+        // Expect a onResourcesAvailable().
+        mCanonical.flushCallbacks();
+        verify(globalCallback).onResourcesAvailable();
+    }
+
+    @Test
+    public void testConcurrentCaptureReject() throws Exception {
+        assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+                || mSupportConcurrentCapture);
+        verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+        // Register global callback.
+        ISoundTriggerHal.GlobalCallback globalCallback = mock(
+                ISoundTriggerHal.GlobalCallback.class);
+        mCanonical.registerCallback(globalCallback);
+
+        // Load (this registers the callback).
+        ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+                ISoundTriggerHal.ModelCallback.class);
+        final int handle = loadGenericModel(canonicalCallback);
+
+        // Report external capture active.
+        mCaptureStateNotifier.setState(true);
+
+        // Then start.
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
+        try {
+            mCanonical.startRecognition(handle, 203, 204, config);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+
+        // Deactivate external capture.
+        mCaptureStateNotifier.setState(false);
+
+        // Expect a onResourcesAvailable().
+        mCanonical.flushCallbacks();
+        verify(globalCallback).onResourcesAvailable();
+    }
+
+    @Test
+    public void testStopRecognition() throws Exception {
+        mCanonical.stopRecognition(17);
+        verify(mHalDriver).stopRecognition(17);
+    }
+
+    @Test
+    public void testForceRecognition() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw) {
+            android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver_2_2 =
+                    (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+            mCanonical.forceRecognitionEvent(14);
+            verify(driver_2_2).getModelState(14);
+        } else {
+            try {
+                mCanonical.forceRecognitionEvent(14);
+                fail("Expected an exception");
+            } catch (RecoverableException e) {
+                assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+            }
+        }
+    }
+
+    @Test
+    public void testGetParameter() throws Exception {
+        assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback resultCallback =
+                    invocation.getArgument(2);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, 99);
+            return null;
+        }).when(driver_2_3).getParameter(eq(21), eq(47), any());
+
+        assertEquals(99, mCanonical.getModelParameter(21, 47));
+        verify(driver_2_3).getParameter(eq(21), eq(47), any());
+    }
+
+    @Test
+    public void testSetParameter() throws Exception {
+        assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        mCanonical.setModelParameter(212, 247, 80);
+        verify(driver_2_3).setParameter(212, 247, 80);
+    }
+
+    @Test
+    public void testQueryParameterSupported() throws Exception {
+        assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+                    resultCallback = invocation.getArgument(2);
+
+            // This is the return of this method.
+            android.hardware.soundtrigger.V2_3.ModelParameterRange range =
+                    new android.hardware.soundtrigger.V2_3.ModelParameterRange();
+            range.start = 34;
+            range.end = 45;
+            android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+                    new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+            optionalRange.range(range);
+            resultCallback.onValues(0, optionalRange);
+            return null;
+        }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+        ModelParameterRange range = mCanonical.queryParameter(11, 12);
+        assertNotNull(range);
+        assertEquals(34, range.minInclusive);
+        assertEquals(45, range.maxInclusive);
+        verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+    }
+
+    @Test
+    public void testQueryParameterNotSupported() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+                    (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+            doAnswer(invocation -> {
+                android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+                        resultCallback = invocation.getArgument(2);
+
+                // This is the return of this method.
+                android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+                        new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+                resultCallback.onValues(0, optionalRange);
+                return null;
+            }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+            ModelParameterRange range = mCanonical.queryParameter(11, 12);
+            assertNull(range);
+            verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+        } else {
+            ModelParameterRange range = mCanonical.queryParameter(11, 12);
+            assertNull(range);
+        }
+    }
+
+    private void testGlobalCallback_2_0() {
+        ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+                ISoundTriggerHal.GlobalCallback.class);
+        mCanonical.registerCallback(canonicalCallback);
+        // We just care that it doesn't throw.
+    }
+
+    private void testGlobalCallback_2_4() throws Exception {
+        android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+                (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+        ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+                ISoundTriggerHal.GlobalCallback.class);
+        mCanonical.registerCallback(canonicalCallback);
+
+        ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback>
+                callbackCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.class);
+        verify(driver_2_4).registerGlobalCallback(callbackCaptor.capture());
+        validateGlobalCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+    }
+
+    @Test
+    public void testGlobalCallback() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+            testGlobalCallback_2_4();
+        } else {
+            testGlobalCallback_2_0();
+        }
+    }
+
+    @Test
+    public void testLinkToDeath() throws Exception {
+        IBinder.DeathRecipient canonicalRecipient = mock(IBinder.DeathRecipient.class);
+        when(mHalDriver.linkToDeath(any(), anyLong())).thenReturn(true);
+        mCanonical.linkToDeath(canonicalRecipient);
+
+        ArgumentCaptor<IHwBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
+                IHwBinder.DeathRecipient.class);
+        ArgumentCaptor<Long> cookieCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mHalDriver).linkToDeath(recipientCaptor.capture(), cookieCaptor.capture());
+
+        recipientCaptor.getValue().serviceDied(cookieCaptor.getValue());
+        mCanonical.flushCallbacks();
+        verify(canonicalRecipient).binderDied();
+
+        mCanonical.unlinkToDeath(canonicalRecipient);
+        verify(mHalDriver).unlinkToDeath(recipientCaptor.getValue());
+    }
+
+    @Test
+    public void testInterfaceDescriptor() throws Exception {
+        when(mHalDriver.interfaceDescriptor()).thenReturn("ABCD");
+        assertEquals("ABCD", mCanonical.interfaceDescriptor());
+        verify(mHalDriver).interfaceDescriptor();
+    }
+
+    private void validateGlobalCallback_2_4(
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback hwCallback,
+            ISoundTriggerHal.GlobalCallback canonicalCallback) throws Exception {
+        hwCallback.onResourcesAvailable();
+        mCanonical.flushCallbacks();
+        verify(canonicalCallback).onResourcesAvailable();
+    }
+
+    private void validateCallback_2_0(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
+            ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+        {
+            final int handle = 85;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+            ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    RecognitionEvent.class);
+
+            hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+        }
+
+        {
+            final int handle = 92;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+            ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    PhraseRecognitionEvent.class);
+
+            hwCallback.phraseRecognitionCallback(
+                    TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+                    RecognitionStatus.SUCCESS);
+        }
+        verifyNoMoreInteractions(canonicalCallback);
+        clearInvocations(canonicalCallback);
+    }
+
+    private void validateCallback_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback,
+            ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+        {
+            final int handle = 85;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+            ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    RecognitionEvent.class);
+
+            hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+                    99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+        }
+
+        {
+            final int handle = 92;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+            ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    PhraseRecognitionEvent.class);
+
+            hwCallback.phraseRecognitionCallback_2_1(
+                    TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+                    RecognitionStatus.SUCCESS);
+        }
+        verifyNoMoreInteractions(canonicalCallback);
+        clearInvocations(canonicalCallback);
+    }
+
+    private void validateCallback_2_4(
+            android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback hwCallback,
+            ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+        {
+            final int handle = 85;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+            ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    RecognitionEvent.class);
+
+            hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+                    99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+        }
+
+        {
+            final int handle = 92;
+            final int status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+            ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                    PhraseRecognitionEvent.class);
+
+            hwCallback.phraseRecognitionCallback_2_1(
+                    TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+            TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+                    RecognitionStatus.SUCCESS);
+        }
+
+        {
+            final int handle = 23;
+            hwCallback.modelUnloaded(handle);
+            mCanonical.flushCallbacks();
+            verify(canonicalCallback).modelUnloaded(handle);
+        }
+        verifyNoMoreInteractions(canonicalCallback);
+        clearInvocations(canonicalCallback);
+    }
+
+    public static class CaptureStateNotifier implements ICaptureStateNotifier {
+        private final List<Listener> mListeners = new LinkedList<>();
+
+        @Override
+        public boolean registerListener(Listener listener) {
+            mListeners.add(listener);
+            return false;
+        }
+
+        @Override
+        public void unregisterListener(Listener listener) {
+            mListeners.remove(listener);
+        }
+
+        public void setState(boolean state) {
+            for (Listener listener : mListeners) {
+                listener.onCaptureStateChange(state);
+            }
+        }
+
+        public void verifyNoMoreListeners() {
+            assertEquals(0, mListeners.size());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 509eb25..1daf831 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -16,89 +16,50 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import static org.junit.Assert.assertArrayEquals;
 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.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.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.hardware.audio.common.V2_0.AudioConfig;
-import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
-import android.media.audio.common.AudioChannelMask;
-import android.media.audio.common.AudioFormat;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.os.HidlMemoryUtil;
-import android.os.HwParcel;
-import android.os.IHwBinder;
-import android.os.IHwInterface;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.SharedMemory;
-import android.system.ErrnoException;
 import android.util.Pair;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
 
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-
-@RunWith(Parameterized.class)
+@RunWith(JUnit4.class)
 public class SoundTriggerMiddlewareImplTest {
-    private static final String TAG = "SoundTriggerMiddlewareImplTest";
+    @Mock public ISoundTriggerHal mHalDriver = mock(ISoundTriggerHal.class);
 
-    // We run the test once for every version of the underlying driver.
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[]{
-                mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
-                mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
-                mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
-                mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
-        };
-    }
-
-    @Mock
-    @Parameterized.Parameter
-    public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
-
-    @Mock
-    private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
-            SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
+    @Mock private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider =
+            mock(SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
 
     private SoundTriggerMiddlewareImpl mService;
 
@@ -106,522 +67,41 @@
         return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
     }
 
-    private static SoundModel createGenericSoundModel() {
-        return createSoundModel(SoundModelType.GENERIC);
-    }
-
-    private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
-        try {
-            SharedMemory shmem = SharedMemory.create("", data.length);
-            ByteBuffer buffer = shmem.mapReadWrite();
-            buffer.put(data);
-            return shmem.getFileDescriptor();
-        } catch (ErrnoException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static SoundModel createSoundModel(int type) {
-        SoundModel model = new SoundModel();
-        model.type = type;
-        model.uuid = "12345678-2345-3456-4567-abcdef987654";
-        model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
-        byte[] data = new byte[]{91, 92, 93, 94, 95};
-        model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
-        model.dataSize = data.length;
-        return model;
-    }
-
-    private static PhraseSoundModel createPhraseSoundModel() {
-        PhraseSoundModel model = new PhraseSoundModel();
-        model.common = createSoundModel(SoundModelType.KEYPHRASE);
-        model.phrases = new Phrase[1];
-        model.phrases[0] = new Phrase();
-        model.phrases[0].id = 123;
-        model.phrases[0].users = new int[]{5, 6, 7};
-        model.phrases[0].locale = "locale";
-        model.phrases[0].text = "text";
-        model.phrases[0].recognitionModes =
-                RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
-        return model;
-    }
-
-    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
-            boolean supportConcurrentCapture) {
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
-                new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
-        properties.implementor = "implementor";
-        properties.description = "description";
-        properties.version = 123;
-        properties.uuid = new Uuid();
-        properties.uuid.timeLow = 1;
-        properties.uuid.timeMid = 2;
-        properties.uuid.versionAndTimeHigh = 3;
-        properties.uuid.variantAndClockSeqHigh = 4;
-        properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
-
-        properties.maxSoundModels = 456;
-        properties.maxKeyPhrases = 567;
-        properties.maxUsers = 678;
-        properties.recognitionModes =
-                android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
-                | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
-                | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
-                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
-        properties.captureTransition = true;
-        properties.maxBufferMs = 321;
-        properties.concurrentCapture = supportConcurrentCapture;
-        properties.triggerInEvent = true;
-        properties.powerConsumptionMw = 432;
-        return properties;
-    }
-
-    private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
-            boolean supportConcurrentCapture) {
-        android.hardware.soundtrigger.V2_3.Properties properties =
-                new android.hardware.soundtrigger.V2_3.Properties();
-        properties.base = createDefaultProperties(supportConcurrentCapture);
-        properties.supportedModelArch = "supportedModelArch";
-        properties.audioCapabilities =
-                android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
-                        | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
-        return properties;
-    }
-
-    private void validateDefaultProperties(SoundTriggerModuleProperties properties,
-            boolean supportConcurrentCapture) {
-        assertEquals("implementor", properties.implementor);
-        assertEquals("description", properties.description);
-        assertEquals(123, properties.version);
-        assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
-        assertEquals(456, properties.maxSoundModels);
-        assertEquals(567, properties.maxKeyPhrases);
-        assertEquals(678, properties.maxUsers);
-        assertEquals(RecognitionMode.GENERIC_TRIGGER
-                | RecognitionMode.USER_AUTHENTICATION
-                | RecognitionMode.USER_IDENTIFICATION
-                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
-        assertTrue(properties.captureTransition);
-        assertEquals(321, properties.maxBufferMs);
-        assertEquals(supportConcurrentCapture, properties.concurrentCapture);
-        assertTrue(properties.triggerInEvent);
-        assertEquals(432, properties.powerConsumptionMw);
-
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            assertEquals("supportedModelArch", properties.supportedModelArch);
-            assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
-                    properties.audioCapabilities);
-        } else {
-            assertEquals("", properties.supportedModelArch);
-            assertEquals(0, properties.audioCapabilities);
-        }
-    }
-
-    private void verifyNotGetProperties() throws RemoteException {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
-                    never()).getProperties(any());
-        }
-    }
-
-    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
-            int hwHandle,
-            int status) {
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
-                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
-        halEvent.status = status;
-        halEvent.type = SoundModelType.GENERIC;
-        halEvent.model = hwHandle;
-        halEvent.captureAvailable = true;
-        // This field is ignored.
-        halEvent.captureSession = 123;
-        halEvent.captureDelayMs = 234;
-        halEvent.capturePreambleMs = 345;
-        halEvent.triggerInData = true;
-        halEvent.audioConfig = new AudioConfig();
-        halEvent.audioConfig.sampleRateHz = 456;
-        halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
-        halEvent.audioConfig.format = AudioFormat.MP3;
-        // hwEvent.audioConfig.offloadInfo is irrelevant.
-        halEvent.data.add((byte) 31);
-        halEvent.data.add((byte) 32);
-        halEvent.data.add((byte) 33);
-        return halEvent;
-    }
-
-    private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
-            int hwHandle,
-            int status) {
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
-                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
-        halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
-        halEvent.header.data.clear();
-        halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
-        return halEvent;
-    }
-
-    private static void validateRecognitionEvent(RecognitionEvent event, int status) {
-        assertEquals(status, event.status);
-        assertEquals(SoundModelType.GENERIC, event.type);
-        assertTrue(event.captureAvailable);
-        assertEquals(101, event.captureSession);
-        assertEquals(234, event.captureDelayMs);
-        assertEquals(345, event.capturePreambleMs);
-        assertTrue(event.triggerInData);
-        assertEquals(456, event.audioConfig.sampleRateHz);
-        assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
-        assertEquals(AudioFormat.MP3, event.audioConfig.format);
-    }
-
-    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
-            int hwHandle, int status) {
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
-                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
-        halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
-
-        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
-                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
-        halExtra.id = 123;
-        halExtra.confidenceLevel = 52;
-        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
-                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
-        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
-                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
-        halLevel.userId = 31;
-        halLevel.levelPercent = 43;
-        halExtra.levels.add(halLevel);
-        halEvent.phraseExtras.add(halExtra);
-        return halEvent;
-    }
-
-    private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
-            int hwHandle, int status) {
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
-                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
-        halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
-
-        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
-                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
-        halExtra.id = 123;
-        halExtra.confidenceLevel = 52;
-        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
-                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
-        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
-                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
-        halLevel.userId = 31;
-        halLevel.levelPercent = 43;
-        halExtra.levels.add(halLevel);
-        halEvent.phraseExtras.add(halExtra);
-        return halEvent;
-    }
-
-    private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
-        validateRecognitionEvent(event.common, status);
-
-        assertEquals(1, event.phraseExtras.length);
-        assertEquals(123, event.phraseExtras[0].id);
-        assertEquals(52, event.phraseExtras[0].confidenceLevel);
-        assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
-                event.phraseExtras[0].recognitionModes);
-        assertEquals(1, event.phraseExtras[0].levels.length);
-        assertEquals(31, event.phraseExtras[0].levels[0].userId);
-        assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
-    }
-
-    private void initService(boolean supportConcurrentCapture) throws RemoteException {
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
-                    createDefaultProperties(
-                            supportConcurrentCapture);
-            ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
-                    0)).onValues(0,
-                    properties);
-            return null;
-        }).when(mHalDriver).getProperties(any());
-
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                    (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-            doAnswer(invocation -> {
-                android.hardware.soundtrigger.V2_3.Properties properties =
-                        createDefaultProperties_2_3(
-                                supportConcurrentCapture);
-                ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
-                        invocation.getArgument(
-                        0)).onValues(0,
-                        properties);
-                return null;
-            }).when(driver).getProperties_2_3(any());
-        }
-
-        mService = new SoundTriggerMiddlewareImpl(() -> {
-            return mHalDriver;
-        }, mAudioSessionProvider);
-    }
-
-    private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
-            int hwHandle) throws RemoteException {
-        SoundModel model = createGenericSoundModel();
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
-                    invocation.getArgument(1);
-            int callbackCookie = invocation.getArgument(2);
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
-                    resultCallback = invocation.getArgument(3);
-
-            // This is the return of this method.
-            resultCallback.onValues(0, hwHandle);
-
-            // This is the async mCallback that comes after.
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
-                    new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
-            modelEvent.status =
-                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
-            modelEvent.model = hwHandle;
-            callback.soundModelCallback(modelEvent, callbackCookie);
-            return null;
-        }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
-                cookieCaptor.capture(), any());
-
-        when(mAudioSessionProvider.acquireSession()).thenReturn(
-                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
-        int handle = module.loadModel(model);
-        verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
-        verify(mAudioSessionProvider).acquireSession();
-
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
-                modelCaptor.getValue();
-        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
-                hidlModel.type);
-        assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
-        assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
-        assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
-
-        return new Pair<>(handle,
-                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
-    }
-
-    private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_1(ISoundTriggerModule module,
-            int hwHandle) throws RemoteException {
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-        SoundModel model = createGenericSoundModel();
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
-                    invocation.getArgument(1);
-            int callbackCookie = invocation.getArgument(2);
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
-                    resultCallback = invocation.getArgument(3);
-
-            // This is the return of this method.
-            resultCallback.onValues(0, hwHandle);
-
-            // This is the async mCallback that comes after.
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
-                    new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
-            modelEvent.header.status =
-                    android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
-            modelEvent.header.model = hwHandle;
-            callback.soundModelCallback_2_1(modelEvent, callbackCookie);
-            return null;
-        }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                cookieCaptor.capture(), any());
-
-        when(mAudioSessionProvider.acquireSession()).thenReturn(
-                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
-        int handle = module.loadModel(model);
-        verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
-        verify(mAudioSessionProvider).acquireSession();
-
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
-                modelCaptor.getValue();
-        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
-                hidlModel.header.type);
-        assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
-        assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
-        assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
-                HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
-
-        return new Pair<>(handle,
-                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
-    }
-
     private Pair<Integer, SoundTriggerHwCallback> loadGenericModel(ISoundTriggerModule module,
             int hwHandle) throws RemoteException {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
-            return loadGenericModel_2_1(module, hwHandle);
-        } else {
-            return loadGenericModel_2_0(module, hwHandle);
-        }
+        SoundModel model = TestUtil.createGenericSoundModel();
+        ArgumentCaptor<SoundModel> modelCaptor = ArgumentCaptor.forClass(SoundModel.class);
+        ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ISoundTriggerHal.ModelCallback.class);
+
+        when(mHalDriver.loadSoundModel(any(), any())).thenReturn(hwHandle);
+        when(mAudioSessionProvider.acquireSession()).thenReturn(
+                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+        int handle = module.loadModel(model);
+        verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture());
+        verify(mAudioSessionProvider).acquireSession();
+        assertEquals(model, modelCaptor.getValue());
+        return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
     }
 
-    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_0(ISoundTriggerModule module,
+    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(ISoundTriggerModule module,
             int hwHandle) throws RemoteException {
-        PhraseSoundModel model = createPhraseSoundModel();
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
-                modelCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+        PhraseSoundModel model = TestUtil.createPhraseSoundModel();
+        ArgumentCaptor<PhraseSoundModel> modelCaptor = ArgumentCaptor.forClass(
+                PhraseSoundModel.class);
+        ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+                ISoundTriggerHal.ModelCallback.class);
 
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
-                    invocation.getArgument(
-                            1);
-            int callbackCookie = invocation.getArgument(2);
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
-                    resultCallback =
-                    invocation.getArgument(
-                            3);
-
-            // This is the return of this method.
-            resultCallback.onValues(0, hwHandle);
-
-            // This is the async mCallback that comes after.
-            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
-                    new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
-            modelEvent.status =
-                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
-            modelEvent.model = hwHandle;
-            callback.soundModelCallback(modelEvent, callbackCookie);
-            return null;
-        }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
-                cookieCaptor.capture(), any());
-
+        when(mHalDriver.loadPhraseSoundModel(any(), any())).thenReturn(hwHandle);
         when(mAudioSessionProvider.acquireSession()).thenReturn(
                 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
 
         int handle = module.loadPhraseModel(model);
-        verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+        verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture());
         verify(mAudioSessionProvider).acquireSession();
-
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
-                modelCaptor.getValue();
-
-        // Validate common part.
-        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
-                hidlModel.common.type);
-        assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
-        assertEquals(model.common.vendorUuid,
-                ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
-        assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
-
-        // Validate phrase part.
-        assertEquals(1, hidlModel.phrases.size());
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
-                hidlModel.phrases.get(0);
-        assertEquals(123, hidlPhrase.id);
-        assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
-        assertEquals("locale", hidlPhrase.locale);
-        assertEquals("text", hidlPhrase.text);
-        assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
-                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
-                hidlPhrase.recognitionModes);
-
-        return new Pair<>(handle,
-                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
-    }
-
-    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_1(ISoundTriggerModule module,
-            int hwHandle) throws RemoteException {
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
-        PhraseSoundModel model = createPhraseSoundModel();
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
-                modelCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
-        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
-                    invocation.getArgument(
-                            1);
-            int callbackCookie = invocation.getArgument(2);
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
-                    resultCallback =
-                    invocation.getArgument(
-                            3);
-
-            // This is the return of this method.
-            resultCallback.onValues(0, hwHandle);
-
-            // This is the async mCallback that comes after.
-            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
-                    new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
-            modelEvent.header.status =
-                    android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
-            modelEvent.header.model = hwHandle;
-            callback.soundModelCallback_2_1(modelEvent, callbackCookie);
-            return null;
-        }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                cookieCaptor.capture(), any());
-
-        when(mAudioSessionProvider.acquireSession()).thenReturn(
-                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
-        int handle = module.loadPhraseModel(model);
-        verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
-        verify(mAudioSessionProvider).acquireSession();
-
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
-                modelCaptor.getValue();
-
-        // Validate common part.
-        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
-                hidlModel.common.header.type);
-        assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
-        assertEquals(model.common.vendorUuid,
-                ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
-        assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
-                HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
-
-        // Validate phrase part.
-        assertEquals(1, hidlModel.phrases.size());
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
-                hidlModel.phrases.get(0);
-        assertEquals(123, hidlPhrase.id);
-        assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
-        assertEquals("locale", hidlPhrase.locale);
-        assertEquals("text", hidlPhrase.text);
-        assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
-                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
-                hidlPhrase.recognitionModes);
-
-        return new Pair<>(handle,
-                new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
-    }
-
-    private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(
-            ISoundTriggerModule module, int hwHandle) throws RemoteException {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
-            return loadPhraseModel_2_1(module, hwHandle);
-        } else {
-            return loadPhraseModel_2_0(module, hwHandle);
-        }
+        assertEquals(model, modelCaptor.getValue());
+        return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
     }
 
     private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
@@ -631,204 +111,35 @@
         verify(mAudioSessionProvider).releaseSession(101);
     }
 
-    private void startRecognition_2_0(ISoundTriggerModule module, int handle,
-            int hwHandle) throws RemoteException {
-        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
-                configCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+    private void startRecognition(ISoundTriggerModule module, int handle, int hwHandle)
+            throws RemoteException {
+        ArgumentCaptor<RecognitionConfig> configCaptor = ArgumentCaptor.forClass(
+                RecognitionConfig.class);
 
-        when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
-                .thenReturn(0);
-
-        RecognitionConfig config = createRecognitionConfig();
+        RecognitionConfig config = TestUtil.createRecognitionConfig();
 
         module.startRecognition(handle, config);
-        verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
-
-        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
-                configCaptor.getValue();
-        assertTrue(halConfig.captureRequested);
-        assertEquals(102, halConfig.captureHandle);
-        assertEquals(103, halConfig.captureDevice);
-        assertEquals(1, halConfig.phrases.size());
-        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
-                halConfig.phrases.get(0);
-        assertEquals(123, halPhraseExtra.id);
-        assertEquals(4, halPhraseExtra.confidenceLevel);
-        assertEquals(5, halPhraseExtra.recognitionModes);
-        assertEquals(1, halPhraseExtra.levels.size());
-        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
-        assertEquals(234, halLevel.userId);
-        assertEquals(34, halLevel.levelPercent);
-        assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
-    }
-
-    private void startRecognition_2_1(ISoundTriggerModule module, int handle,
-            int hwHandle) throws RemoteException {
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
-                configCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
-
-        when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
-                .thenReturn(0);
-
-        RecognitionConfig config = createRecognitionConfig();
-
-        module.startRecognition(handle, config);
-        verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
-
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
-                configCaptor.getValue();
-        assertTrue(halConfig.header.captureRequested);
-        assertEquals(102, halConfig.header.captureHandle);
-        assertEquals(103, halConfig.header.captureDevice);
-        assertEquals(1, halConfig.header.phrases.size());
-        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
-                halConfig.header.phrases.get(0);
-        assertEquals(123, halPhraseExtra.id);
-        assertEquals(4, halPhraseExtra.confidenceLevel);
-        assertEquals(5, halPhraseExtra.recognitionModes);
-        assertEquals(1, halPhraseExtra.levels.size());
-        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
-        assertEquals(234, halLevel.userId);
-        assertEquals(34, halLevel.levelPercent);
-        assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
-                HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
-    }
-
-    private void startRecognition_2_3(ISoundTriggerModule module, int handle,
-            int hwHandle) throws RemoteException {
-        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
-        ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig>
-                configCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
-
-        when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0);
-
-        RecognitionConfig config = createRecognitionConfig();
-
-        module.startRecognition(handle, config);
-        verify(driver).startRecognition_2_3(eq(hwHandle), any());
-
-        android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended =
-                configCaptor.getValue();
-        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 =
-                halConfigExtended.base;
-
-        assertTrue(halConfig_2_1.header.captureRequested);
-        assertEquals(102, halConfig_2_1.header.captureHandle);
-        assertEquals(103, halConfig_2_1.header.captureDevice);
-        assertEquals(1, halConfig_2_1.header.phrases.size());
-        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
-                halConfig_2_1.header.phrases.get(0);
-        assertEquals(123, halPhraseExtra.id);
-        assertEquals(4, halPhraseExtra.confidenceLevel);
-        assertEquals(5, halPhraseExtra.recognitionModes);
-        assertEquals(1, halPhraseExtra.levels.size());
-        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
-        assertEquals(234, halLevel.userId);
-        assertEquals(34, halLevel.levelPercent);
-        assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
-                HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data));
-        assertEquals(AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities);
-    }
-
-    private void startRecognition(ISoundTriggerModule module, int handle,
-            int hwHandle) throws RemoteException {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            startRecognition_2_3(module, handle, hwHandle);
-        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
-            startRecognition_2_1(module, handle, hwHandle);
-        } else {
-            startRecognition_2_0(module, handle, hwHandle);
-        }
-    }
-
-    private RecognitionConfig createRecognitionConfig() {
-        RecognitionConfig config = new RecognitionConfig();
-        config.captureRequested = true;
-        config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
-        config.phraseRecognitionExtras[0].id = 123;
-        config.phraseRecognitionExtras[0].confidenceLevel = 4;
-        config.phraseRecognitionExtras[0].recognitionModes = 5;
-        config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
-        config.phraseRecognitionExtras[0].levels[0].userId = 234;
-        config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
-        config.data = new byte[]{5, 4, 3, 2, 1};
-        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
-        return config;
+        verify(mHalDriver).startRecognition(eq(hwHandle), eq(103), eq(102), configCaptor.capture());
+        assertEquals(config, configCaptor.getValue());
     }
 
     private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
             throws RemoteException {
-        when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
         module.stopRecognition(handle);
         verify(mHalDriver).stopRecognition(hwHandle);
     }
 
-    private void verifyNotStartRecognition() throws RemoteException {
-        verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
-            verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
-                    never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
-        } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
-                    never()).startRecognition_2_3(anyInt(), any());
-        }
-    }
-
-
     @Before
     public void setUp() throws Exception {
         clearInvocations(mHalDriver);
         clearInvocations(mAudioSessionProvider);
+        when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties(false));
+        mService = new SoundTriggerMiddlewareImpl(() -> mHalDriver, mAudioSessionProvider);
+    }
 
-        // This binder is associated with the mock, so it can be cast to either version of the
-        // HAL interface.
-        final IHwBinder binder = new IHwBinder() {
-            @Override
-            public void transact(int code, HwParcel request, HwParcel reply, int flags)
-                    throws RemoteException {
-                // This is a little hacky, but a very easy way to gracefully reject a request for
-                // an unsupported interface (after queryLocalInterface() returns null, the client
-                // will attempt a remote transaction to obtain the interface. RemoteException will
-                // cause it to give up).
-                throw new RemoteException();
-            }
-
-            @Override
-            public IHwInterface queryLocalInterface(String descriptor) {
-                if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
-                        || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
-                        && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
-                        || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
-                        && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
-                        || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
-                        && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-                    return mHalDriver;
-                }
-                return null;
-            }
-
-            @Override
-            public boolean linkToDeath(DeathRecipient recipient, long cookie) {
-                return true;
-            }
-
-            @Override
-            public boolean unlinkToDeath(DeathRecipient recipient) {
-                return true;
-            }
-        };
-
-        when(mHalDriver.asBinder()).thenReturn(binder);
+    @After
+    public void tearDown() {
+        verify(mHalDriver, never()).reboot();
     }
 
     @Test
@@ -836,58 +147,28 @@
     }
 
     @Test
-    public void testListModules() throws Exception {
-        initService(true);
+    public void testListModules() {
         // Note: input and output properties are NOT the same type, even though they are in any way
         // equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
         // the service. The service actually performs a (trivial) conversion between the two.
         SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
         assertEquals(1, allDescriptors.length);
 
-        SoundTriggerModuleProperties properties = allDescriptors[0].properties;
-
-        validateDefaultProperties(properties, true);
-        verifyNotGetProperties();
+        Properties properties = allDescriptors[0].properties;
+        assertEquals(TestUtil.createDefaultProperties(false), properties);
     }
 
     @Test
     public void testAttachDetach() throws Exception {
         // Normal attachment / detachment.
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
-        assertNotNull(module);
-        module.detach();
-    }
-
-    @Test
-    public void testAttachDetachNotAvailable() throws Exception {
-        // Attachment / detachment during external capture, with a module not supporting concurrent
-        // capture.
-        initService(false);
-        ISoundTriggerCallback callback = createCallbackMock();
-        ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(false);
-        assertNotNull(module);
-        module.detach();
-    }
-
-    @Test
-    public void testAttachDetachAvailable() throws Exception {
-        // Attachment / detachment during external capture, with a module supporting concurrent
-        // capture.
-        initService(true);
-        ISoundTriggerCallback callback = createCallbackMock();
-        ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
         assertNotNull(module);
         module.detach();
     }
 
     @Test
     public void testLoadUnloadModel() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -898,8 +179,26 @@
     }
 
     @Test
+    public void testLoadPreemptModel() throws Exception {
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        final int hwHandle = 7;
+        Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+
+        int handle = loadResult.first;
+        SoundTriggerHwCallback hwCallback = loadResult.second;
+
+        // Signal preemption.
+        hwCallback.sendUnloadEvent(hwHandle);
+
+        verify(callback).onModelUnloaded(handle);
+
+        module.detach();
+    }
+
+    @Test
     public void testLoadUnloadPhraseModel() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -911,7 +210,6 @@
 
     @Test
     public void testStartStopRecognition() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -931,8 +229,31 @@
     }
 
     @Test
+    public void testStartRecognitionBusy() throws Exception {
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 7;
+        int handle = loadGenericModel(module, hwHandle).first;
+
+        // Start the model.
+        doThrow(new RecoverableException(Status.RESOURCE_CONTENTION)).when(
+                mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+
+        try {
+            RecognitionConfig config = TestUtil.createRecognitionConfig();
+            module.startRecognition(handle, config);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+        }
+
+        verify(mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+    }
+
+    @Test
     public void testStartStopPhraseRecognition() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -953,7 +274,6 @@
 
     @Test
     public void testRecognition() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -967,15 +287,15 @@
         startRecognition(module, handle, hwHandle);
 
         // Signal a capture from the driver.
-        hwCallback.sendRecognitionEvent(hwHandle,
-                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+        RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+                RecognitionStatus.SUCCESS);
 
         ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 RecognitionEvent.class);
-        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
-        validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+        assertEquals(event, eventCaptor.getValue());
 
         // Unload the model.
         unloadModel(module, handle, hwHandle);
@@ -984,7 +304,6 @@
 
     @Test
     public void testPhraseRecognition() throws Exception {
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -998,15 +317,15 @@
         startRecognition(module, handle, hwHandle);
 
         // Signal a capture from the driver.
-        hwCallback.sendPhraseRecognitionEvent(hwHandle,
-                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+        PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+                RecognitionStatus.SUCCESS);
 
         ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 PhraseRecognitionEvent.class);
-        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
-        validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+        assertEquals(event, eventCaptor.getValue());
 
         // Unload the model.
         unloadModel(module, handle, hwHandle);
@@ -1015,14 +334,6 @@
 
     @Test
     public void testForceRecognition() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -1037,18 +348,49 @@
 
         // Force a trigger.
         module.forceRecognitionEvent(handle);
-        verify(driver).getModelState(hwHandle);
+        verify(mHalDriver).forceRecognitionEvent(hwHandle);
 
         // Signal a capture from the driver.
-        // '3' means 'forced', there's no constant for that in the HAL.
-        hwCallback.sendRecognitionEvent(hwHandle, 3);
+        RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+                RecognitionStatus.FORCED);
 
         ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 RecognitionEvent.class);
-        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
-        validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+        assertEquals(event, eventCaptor.getValue());
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testForceRecognitionNotSupported() throws Exception {
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 17;
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
+        int handle = modelHandles.first;
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Force a trigger.
+        doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+                mHalDriver).forceRecognitionEvent(hwHandle);
+        try {
+            module.forceRecognitionEvent(handle);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+        }
 
         // Stop the recognition.
         stopRecognition(module, handle, hwHandle);
@@ -1060,14 +402,6 @@
 
     @Test
     public void testForcePhraseRecognition() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
-        initService(true);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
 
@@ -1082,18 +416,49 @@
 
         // Force a trigger.
         module.forceRecognitionEvent(handle);
-        verify(driver).getModelState(hwHandle);
+        verify(mHalDriver).forceRecognitionEvent(hwHandle);
 
         // Signal a capture from the driver.
-        // '3' means 'forced', there's no constant for that in the HAL.
-        hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
+        PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+                RecognitionStatus.FORCED);
 
         ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 PhraseRecognitionEvent.class);
-        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
-        validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+        assertEquals(event, eventCaptor.getValue());
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testForcePhraseRecognitionNotSupported() throws Exception {
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 17;
+        Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+        int handle = modelHandles.first;
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Force a trigger.
+        doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+                mHalDriver).forceRecognitionEvent(hwHandle);
+        try {
+            module.forceRecognitionEvent(handle);
+            fail("Expected an exception");
+        } catch (RecoverableException e) {
+            assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+        }
 
         // Stop the recognition.
         stopRecognition(module, handle, hwHandle);
@@ -1106,46 +471,28 @@
     @Test
     public void testAbortRecognition() throws Exception {
         // Make sure the HAL doesn't support concurrent capture.
-        initService(false);
-        mService.setCaptureState(false);
-
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
 
         // Load the model.
         final int hwHandle = 11;
-        int handle = loadGenericModel(module, hwHandle).first;
+        Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+        int handle = loadResult.first;
+        SoundTriggerHwCallback hwCallback = loadResult.second;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
 
         // Abort.
-        mService.setCaptureState(true);
+        hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
 
         ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 RecognitionEvent.class);
-        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
         assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
 
-        // Make sure we are notified of the lost availability.
-        verify(callback).onRecognitionAvailabilityChange(false);
-
-        // Attempt to start a new recognition - should get an abort event immediately, without
-        // involving the HAL.
-        clearInvocations(callback);
-        clearInvocations(mHalDriver);
-        module.startRecognition(handle, createRecognitionConfig());
-        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
-        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
-        verifyNotStartRecognition();
-
-        // Now enable it and make sure we are notified.
-        mService.setCaptureState(false);
-        verify(callback).onRecognitionAvailabilityChange(true);
-
         // Unload the model.
         unloadModel(module, handle, hwHandle);
         module.detach();
@@ -1154,298 +501,124 @@
     @Test
     public void testAbortPhraseRecognition() throws Exception {
         // Make sure the HAL doesn't support concurrent capture.
-        initService(false);
-        mService.setCaptureState(false);
-
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
 
         // Load the model.
         final int hwHandle = 11;
-        int handle = loadPhraseModel(module, hwHandle).first;
+        Pair<Integer, SoundTriggerHwCallback> loadResult = loadPhraseModel(module, hwHandle);
+        int handle = loadResult.first;
+        SoundTriggerHwCallback hwCallback = loadResult.second;
 
         // Initiate a recognition.
         startRecognition(module, handle, hwHandle);
 
         // Abort.
-        mService.setCaptureState(true);
+        hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
 
         ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
                 PhraseRecognitionEvent.class);
-        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
 
         // Validate the event.
         assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
 
-        // Make sure we are notified of the lost availability.
-        verify(callback).onRecognitionAvailabilityChange(false);
-
-        // Attempt to start a new recognition - should get an abort event immediately, without
-        // involving the HAL.
-        clearInvocations(callback);
-        clearInvocations(mHalDriver);
-        module.startRecognition(handle, createRecognitionConfig());
-        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
-        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
-        verifyNotStartRecognition();
-
-        // Now enable it and make sure we are notified.
-        mService.setCaptureState(false);
-        verify(callback).onRecognitionAvailabilityChange(true);
-
         // Unload the model.
         unloadModel(module, handle, hwHandle);
         module.detach();
     }
 
     @Test
-    public void testNotAbortRecognitionConcurrent() throws Exception {
-        // Make sure the HAL supports concurrent capture.
-        initService(true);
-
-        ISoundTriggerCallback callback = createCallbackMock();
-        ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
-        clearInvocations(callback);
-
-        // Load the model.
-        final int hwHandle = 13;
-        int handle = loadGenericModel(module, hwHandle).first;
-
-        // Initiate a recognition.
-        startRecognition(module, handle, hwHandle);
-
-        // Signal concurrent capture. Shouldn't abort.
-        mService.setCaptureState(true);
-        verify(callback, never()).onRecognition(anyInt(), any());
-        verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
-
-        // Stop the recognition.
-        stopRecognition(module, handle, hwHandle);
-
-        // Initiating a new one should work fine.
-        clearInvocations(mHalDriver);
-        startRecognition(module, handle, hwHandle);
-        verify(callback, never()).onRecognition(anyInt(), any());
-        stopRecognition(module, handle, hwHandle);
-
-        // Unload the model.
-        module.unloadModel(handle);
-        module.detach();
-    }
-
-    @Test
-    public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
-        // Make sure the HAL supports concurrent capture.
-        initService(true);
-
-        ISoundTriggerCallback callback = createCallbackMock();
-        ISoundTriggerModule module = mService.attach(0, callback);
-        verify(callback).onRecognitionAvailabilityChange(true);
-        clearInvocations(callback);
-
-        // Load the model.
-        final int hwHandle = 13;
-        int handle = loadPhraseModel(module, hwHandle).first;
-
-        // Initiate a recognition.
-        startRecognition(module, handle, hwHandle);
-
-        // Signal concurrent capture. Shouldn't abort.
-        mService.setCaptureState(true);
-        verify(callback, never()).onPhraseRecognition(anyInt(), any());
-        verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
-
-        // Stop the recognition.
-        stopRecognition(module, handle, hwHandle);
-
-        // Initiating a new one should work fine.
-        clearInvocations(mHalDriver);
-        startRecognition(module, handle, hwHandle);
-        verify(callback, never()).onRecognition(anyInt(), any());
-        stopRecognition(module, handle, hwHandle);
-
-        // Unload the model.
-        module.unloadModel(handle);
-        module.detach();
-    }
-
-    @Test
     public void testParameterSupported() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
-        initService(false);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 12;
         int modelHandle = loadGenericModel(module, hwHandle).first;
 
-        doAnswer((Answer<Void>) invocation -> {
-            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
-                    resultCallback = invocation.getArgument(2);
-            android.hardware.soundtrigger.V2_3.ModelParameterRange range =
-                    new android.hardware.soundtrigger.V2_3.ModelParameterRange();
-            range.start = 23;
-            range.end = 45;
-            OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
-            optionalRange.range(range);
-            resultCallback.onValues(0, optionalRange);
-            return null;
-        }).when(driver).queryParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        ModelParameterRange halRange = new ModelParameterRange();
+        halRange.minInclusive = 23;
+        halRange.maxInclusive = 45;
+
+        when(mHalDriver.queryParameter(eq(hwHandle),
+                eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(halRange);
 
         ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
                 ModelParameter.THRESHOLD_FACTOR);
 
-        verify(driver).queryParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
 
-        assertEquals(23, range.minInclusive);
-        assertEquals(45, range.maxInclusive);
-    }
-
-    @Test
-    public void testParameterNotSupportedOld() throws Exception {
-        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
-            return;
-        }
-
-        initService(false);
-        ISoundTriggerCallback callback = createCallbackMock();
-        ISoundTriggerModule module = mService.attach(0, callback);
-        final int hwHandle = 13;
-        int modelHandle = loadGenericModel(module, hwHandle).first;
-
-        ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
-                ModelParameter.THRESHOLD_FACTOR);
-
-        assertNull(range);
+        assertEquals(halRange, range);
     }
 
     @Test
     public void testParameterNotSupported() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
-        initService(false);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 13;
         int modelHandle = loadGenericModel(module, hwHandle).first;
 
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
-                    resultCallback = invocation.getArgument(2);
-            // This is the return of this method.
-            resultCallback.onValues(0, new OptionalModelParameterRange());
-            return null;
-        }).when(driver).queryParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        when(mHalDriver.queryParameter(eq(hwHandle),
+                eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(null);
 
         ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
                 ModelParameter.THRESHOLD_FACTOR);
 
-        verify(driver).queryParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
 
         assertNull(range);
     }
 
     @Test
     public void testGetParameter() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
-        initService(false);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 14;
         int modelHandle = loadGenericModel(module, hwHandle).first;
 
-        doAnswer(invocation -> {
-            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
-                    resultCallback = invocation.getArgument(2);
-            // This is the return of this method.
-            resultCallback.onValues(0, 234);
-            return null;
-        }).when(driver).getParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        when(mHalDriver.getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR)).thenReturn(
+                234);
 
         int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
 
-        verify(driver).getParameter(eq(hwHandle),
-                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+        verify(mHalDriver).getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR);
 
         assertEquals(234, value);
     }
 
     @Test
     public void testSetParameter() throws Exception {
-        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
-            return;
-        }
-
-        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
-                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
-        initService(false);
         ISoundTriggerCallback callback = createCallbackMock();
         ISoundTriggerModule module = mService.attach(0, callback);
         final int hwHandle = 17;
         int modelHandle = loadGenericModel(module, hwHandle).first;
 
-        when(driver.setParameter(hwHandle,
-                android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
-                456)).thenReturn(0);
-
         module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
 
-        verify(driver).setParameter(hwHandle,
-                android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
+        verify(mHalDriver).setModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR, 456);
     }
 
     private static class SoundTriggerHwCallback {
-        private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
-        private final int mCookie;
+        private final ISoundTriggerHal.ModelCallback mCallback;
 
-        SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
-                int cookie) {
+        SoundTriggerHwCallback(ISoundTriggerHal.ModelCallback callback) {
             mCallback = callback;
-            mCookie = cookie;
         }
 
-        private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
-            if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
-                ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
-                        createRecognitionEvent_2_1(hwHandle, status), mCookie);
-            } else {
-                mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
-                        mCookie);
-            }
+        private RecognitionEvent sendRecognitionEvent(int hwHandle, @RecognitionStatus int status) {
+            RecognitionEvent event = TestUtil.createRecognitionEvent(status);
+            mCallback.recognitionCallback(hwHandle, event);
+            return event;
         }
 
-        private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
-            if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
-                ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
-                        createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
-            } else {
-                mCallback.phraseRecognitionCallback(
-                        createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
-            }
+        private PhraseRecognitionEvent sendPhraseRecognitionEvent(int hwHandle,
+                @RecognitionStatus int status) {
+            PhraseRecognitionEvent event = TestUtil.createPhraseRecognitionEvent(status);
+            mCallback.phraseRecognitionCallback(hwHandle, event);
+            return event;
+        }
+
+        private void sendUnloadEvent(int hwHandle) {
+            mCallback.modelUnloaded(hwHandle);
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
new file mode 100644
index 0000000..4eca298
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback;
+import android.media.audio.common.AudioChannelMask;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Test utilities, aimed at generating populated objects of the various types and validating
+ * corresponding objects generated by the system under test.
+ */
+class TestUtil {
+    static SoundModel createGenericSoundModel() {
+        return createSoundModel(SoundModelType.GENERIC);
+    }
+
+    private static SoundModel createSoundModel(@SoundModelType int type) {
+        SoundModel model = new SoundModel();
+        model.type = type;
+        model.uuid = "12345678-2345-3456-4567-abcdef987654";
+        model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+        byte[] data = new byte[]{91, 92, 93, 94, 95};
+        model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
+        model.dataSize = data.length;
+        return model;
+    }
+
+    private static void validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type) {
+        assertEquals(type, model.header.type);
+        assertEquals("12345678-2345-3456-4567-abcdef987654",
+                ConversionUtil.hidl2aidlUuid(model.header.uuid));
+        assertEquals("87654321-5432-6543-7654-456789fedcba",
+                ConversionUtil.hidl2aidlUuid(model.header.vendorUuid));
+        assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+                HidlMemoryUtil.hidlMemoryToByteArray(model.data));
+    }
+
+    private static void validateSoundModel_2_0(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
+        assertEquals(type, model.type);
+        assertEquals("12345678-2345-3456-4567-abcdef987654",
+                ConversionUtil.hidl2aidlUuid(model.uuid));
+        assertEquals("87654321-5432-6543-7654-456789fedcba",
+                ConversionUtil.hidl2aidlUuid(model.vendorUuid));
+        assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, model.data.toArray());
+    }
+
+    static void validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model) {
+        validateSoundModel_2_1(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+    }
+
+    static void validateGenericSoundModel_2_0(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model) {
+        validateSoundModel_2_0(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+    }
+
+    static PhraseSoundModel createPhraseSoundModel() {
+        PhraseSoundModel model = new PhraseSoundModel();
+        model.common = createSoundModel(SoundModelType.KEYPHRASE);
+        model.phrases = new Phrase[1];
+        model.phrases[0] = new Phrase();
+        model.phrases[0].id = 123;
+        model.phrases[0].users = new int[]{5, 6, 7};
+        model.phrases[0].locale = "locale";
+        model.phrases[0].text = "text";
+        model.phrases[0].recognitionModes =
+                RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+        return model;
+    }
+
+    static void validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model) {
+        validateSoundModel_2_1(model.common,
+                android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+        validatePhrases_2_0(model.phrases);
+    }
+
+    static void validatePhraseSoundModel_2_0(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
+        validateSoundModel_2_0(model.common,
+                android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+        validatePhrases_2_0(model.phrases);
+    }
+
+    private static void validatePhrases_2_0(
+            List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases) {
+        assertEquals(1, phrases.size());
+        assertEquals(123, phrases.get(0).id);
+        assertArrayEquals(new Integer[]{5, 6, 7}, phrases.get(0).users.toArray());
+        assertEquals("locale", phrases.get(0).locale);
+        assertEquals("text", phrases.get(0).text);
+        assertEquals(RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION,
+                phrases.get(0).recognitionModes);
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties_2_0(
+            boolean supportConcurrentCapture) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
+        properties.implementor = "implementor";
+        properties.description = "description";
+        properties.version = 123;
+        properties.uuid.timeLow = 1;
+        properties.uuid.timeMid = 2;
+        properties.uuid.versionAndTimeHigh = 3;
+        properties.uuid.variantAndClockSeqHigh = 4;
+        properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
+
+        properties.maxSoundModels = 456;
+        properties.maxKeyPhrases = 567;
+        properties.maxUsers = 678;
+        properties.recognitionModes =
+                android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+                        | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        properties.captureTransition = true;
+        properties.maxBufferMs = 321;
+        properties.concurrentCapture = supportConcurrentCapture;
+        properties.triggerInEvent = true;
+        properties.powerConsumptionMw = 432;
+        return properties;
+    }
+
+    static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+            boolean supportConcurrentCapture) {
+        android.hardware.soundtrigger.V2_3.Properties properties =
+                new android.hardware.soundtrigger.V2_3.Properties();
+        properties.base = createDefaultProperties_2_0(supportConcurrentCapture);
+        properties.supportedModelArch = "supportedModelArch";
+        properties.audioCapabilities =
+                android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
+                        | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
+        return properties;
+    }
+
+    static Properties createDefaultProperties(boolean supportConcurrentCapture) {
+        Properties properties = new Properties();
+        properties.implementor = "implementor";
+        properties.description = "description";
+        properties.version = 123;
+        properties.uuid = "00000001-0002-0003-0004-05060708090a";
+        properties.maxSoundModels = 456;
+        properties.maxKeyPhrases = 567;
+        properties.maxUsers = 678;
+        properties.recognitionModes =
+                RecognitionMode.VOICE_TRIGGER
+                        | RecognitionMode.USER_IDENTIFICATION
+                        | RecognitionMode.USER_AUTHENTICATION
+                        | RecognitionMode.GENERIC_TRIGGER;
+        properties.captureTransition = true;
+        properties.maxBufferMs = 321;
+        properties.concurrentCapture = supportConcurrentCapture;
+        properties.triggerInEvent = true;
+        properties.powerConsumptionMw = 432;
+        properties.supportedModelArch = "supportedModelArch";
+        properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+                | AudioCapabilities.NOISE_SUPPRESSION;
+        return properties;
+    }
+
+    static void validateDefaultProperties(Properties properties,
+            boolean supportConcurrentCapture) {
+        validateDefaultProperties(properties, supportConcurrentCapture,
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+                "supportedModelArch");
+    }
+
+    static void validateDefaultProperties(Properties properties,
+            boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
+            @NonNull String supportedModelArch) {
+        assertEquals("implementor", properties.implementor);
+        assertEquals("description", properties.description);
+        assertEquals(123, properties.version);
+        assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
+        assertEquals(456, properties.maxSoundModels);
+        assertEquals(567, properties.maxKeyPhrases);
+        assertEquals(678, properties.maxUsers);
+        assertEquals(RecognitionMode.GENERIC_TRIGGER
+                | RecognitionMode.USER_AUTHENTICATION
+                | RecognitionMode.USER_IDENTIFICATION
+                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+        assertTrue(properties.captureTransition);
+        assertEquals(321, properties.maxBufferMs);
+        assertEquals(supportConcurrentCapture, properties.concurrentCapture);
+        assertTrue(properties.triggerInEvent);
+        assertEquals(432, properties.powerConsumptionMw);
+        assertEquals(supportedModelArch, properties.supportedModelArch);
+        assertEquals(audioCapabilities, properties.audioCapabilities);
+    }
+
+    static RecognitionConfig createRecognitionConfig() {
+        RecognitionConfig config = new RecognitionConfig();
+        config.captureRequested = true;
+        config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
+        config.phraseRecognitionExtras[0].id = 123;
+        config.phraseRecognitionExtras[0].confidenceLevel = 4;
+        config.phraseRecognitionExtras[0].recognitionModes = 5;
+        config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
+        config.phraseRecognitionExtras[0].levels[0].userId = 234;
+        config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
+        config.data = new byte[]{5, 4, 3, 2, 1};
+        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+                | AudioCapabilities.NOISE_SUPPRESSION;
+        return config;
+    }
+
+    static void validateRecognitionConfig_2_0(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config,
+            int captureDevice, int captureHandle) {
+        assertTrue(config.captureRequested);
+        assertEquals(captureDevice, config.captureDevice);
+        assertEquals(captureHandle, config.captureHandle);
+        assertEquals(1, config.phrases.size());
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+                config.phrases.get(0);
+        assertEquals(123, halPhraseExtra.id);
+        assertEquals(4, halPhraseExtra.confidenceLevel);
+        assertEquals(5, halPhraseExtra.recognitionModes);
+        assertEquals(1, halPhraseExtra.levels.size());
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+        assertEquals(234, halLevel.userId);
+        assertEquals(34, halLevel.levelPercent);
+        assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, config.data.toArray());
+    }
+
+    static void validateRecognitionConfig_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            int captureDevice, int captureHandle) {
+        assertTrue(config.header.captureRequested);
+        assertEquals(captureDevice, config.header.captureDevice);
+        assertEquals(captureHandle, config.header.captureHandle);
+        assertEquals(1, config.header.phrases.size());
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+                config.header.phrases.get(0);
+        assertEquals(123, halPhraseExtra.id);
+        assertEquals(4, halPhraseExtra.confidenceLevel);
+        assertEquals(5, halPhraseExtra.recognitionModes);
+        assertEquals(1, halPhraseExtra.levels.size());
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+        assertEquals(234, halLevel.userId);
+        assertEquals(34, halLevel.levelPercent);
+        assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+                HidlMemoryUtil.hidlMemoryToByteArray(config.data));
+    }
+
+    static void validateRecognitionConfig_2_3(
+            android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice,
+            int captureHandle) {
+        validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
+
+        assertEquals(AudioCapabilities.ECHO_CANCELLATION
+                | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
+            int hwHandle,
+            int status) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
+        halEvent.status = status;
+        halEvent.type = SoundModelType.GENERIC;
+        halEvent.model = hwHandle;
+        halEvent.captureAvailable = true;
+        // This field is ignored.
+        halEvent.captureSession = 9999;
+        halEvent.captureDelayMs = 234;
+        halEvent.capturePreambleMs = 345;
+        halEvent.triggerInData = true;
+        halEvent.audioConfig.sampleRateHz = 456;
+        halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+        halEvent.audioConfig.format = AudioFormat.MP3;
+        // hwEvent.audioConfig.offloadInfo is irrelevant.
+        halEvent.data.add((byte) 31);
+        halEvent.data.add((byte) 32);
+        halEvent.data.add((byte) 33);
+        return halEvent;
+    }
+
+    static RecognitionEvent createRecognitionEvent(@RecognitionStatus int status) {
+        RecognitionEvent event = new RecognitionEvent();
+        event.status = status;
+        event.type = SoundModelType.GENERIC;
+        event.captureAvailable = true;
+        event.captureDelayMs = 234;
+        event.capturePreambleMs = 345;
+        event.triggerInData = true;
+        event.audioConfig = new AudioConfig();
+        event.audioConfig.sampleRateHz = 456;
+        event.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+        event.audioConfig.format = AudioFormat.MP3;
+        //event.audioConfig.offloadInfo is irrelevant.
+        event.data = new byte[]{31, 32, 33};
+        return event;
+    }
+
+    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
+            int hwHandle,
+            int status) {
+        ISoundTriggerHwCallback.RecognitionEvent halEvent =
+                new ISoundTriggerHwCallback.RecognitionEvent();
+        halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
+        halEvent.header.data.clear();
+        halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
+        return halEvent;
+    }
+
+    static void validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status) {
+        assertEquals(status, event.status);
+        assertEquals(SoundModelType.GENERIC, event.type);
+        assertTrue(event.captureAvailable);
+        assertEquals(234, event.captureDelayMs);
+        assertEquals(345, event.capturePreambleMs);
+        assertTrue(event.triggerInData);
+        assertEquals(456, event.audioConfig.sampleRateHz);
+        assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
+        assertEquals(AudioFormat.MP3, event.audioConfig.format);
+        assertArrayEquals(new byte[]{31, 32, 33}, event.data);
+    }
+
+    static PhraseRecognitionEvent createPhraseRecognitionEvent(@RecognitionStatus int status) {
+        PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+        event.common = createRecognitionEvent(status);
+
+        PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
+        extra.id = 123;
+        extra.confidenceLevel = 52;
+        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
+                | RecognitionMode.GENERIC_TRIGGER;
+        ConfidenceLevel level = new ConfidenceLevel();
+        level.userId = 31;
+        level.levelPercent = 43;
+        extra.levels = new ConfidenceLevel[]{level};
+        event.phraseExtras = new PhraseRecognitionExtra[]{extra};
+        return event;
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
+    createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+        halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
+
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+        halExtra.id = 123;
+        halExtra.confidenceLevel = 52;
+        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+        halLevel.userId = 31;
+        halLevel.levelPercent = 43;
+        halExtra.levels.add(halLevel);
+        halEvent.phraseExtras.add(halExtra);
+        return halEvent;
+    }
+
+    static ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
+            int hwHandle, int status) {
+        ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+                new ISoundTriggerHwCallback.PhraseRecognitionEvent();
+        halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
+
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+        halExtra.id = 123;
+        halExtra.confidenceLevel = 52;
+        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+        halLevel.userId = 31;
+        halLevel.levelPercent = 43;
+        halExtra.levels.add(halLevel);
+        halEvent.phraseExtras.add(halExtra);
+        return halEvent;
+    }
+
+    static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event,
+            @RecognitionStatus int status) {
+        validateRecognitionEvent(event.common, status);
+
+        assertEquals(1, event.phraseExtras.length);
+        assertEquals(123, event.phraseExtras[0].id);
+        assertEquals(52, event.phraseExtras[0].confidenceLevel);
+        assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
+                event.phraseExtras[0].recognitionModes);
+        assertEquals(1, event.phraseExtras[0].levels.length);
+        assertEquals(31, event.phraseExtras[0].levels[0].userId);
+        assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
+    }
+
+    private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
+        try {
+            SharedMemory shmem = SharedMemory.create("", data.length);
+            ByteBuffer buffer = shmem.mapReadWrite();
+            buffer.put(data);
+            return shmem.getFileDescriptor();
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTest.java
new file mode 100644
index 0000000..546b84a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTest.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.util.ArraySet;
+import android.util.SparseLongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.tare.Agent.ActionAffordabilityNote;
+import com.android.server.tare.Agent.OngoingEvent;
+import com.android.server.tare.Agent.TrendCalculator;
+import com.android.server.tare.EconomyManagerInternal.ActionBill;
+import com.android.server.tare.EconomyManagerInternal.AffordabilityChangeListener;
+import com.android.server.tare.EconomyManagerInternal.AnticipatedAction;
+
+import libcore.util.EmptyArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/** Tests various aspects of the Agent. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AgentTest {
+
+    private MockEconomicPolicy mEconomicPolicy;
+
+    private static class MockEconomicPolicy extends EconomicPolicy {
+        private final SparseLongArray mEventCosts = new SparseLongArray();
+
+        MockEconomicPolicy(InternalResourceService irs) {
+            super(irs);
+        }
+
+        @Override
+        long getMinSatiatedBalance(int userId, String pkgName) {
+            return 0;
+        }
+
+        @Override
+        long getMaxSatiatedBalance() {
+            return 0;
+        }
+
+        @Override
+        long getMaxSatiatedCirculation() {
+            return 0;
+        }
+
+        @Override
+        int[] getCostModifiers() {
+            return EmptyArray.INT;
+        }
+
+        @Override
+        Action getAction(int actionId) {
+            if (mEventCosts.indexOfKey(actionId) < 0) {
+                return null;
+            }
+            return new Action(actionId, 0, mEventCosts.get(actionId));
+        }
+
+        @Override
+        Reward getReward(int rewardId) {
+            if (mEventCosts.indexOfKey(rewardId) < 0) {
+                return null;
+            }
+            return new Reward(rewardId, mEventCosts.get(rewardId), mEventCosts.get(rewardId),
+                    10 * mEventCosts.get(rewardId));
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mEconomicPolicy = new MockEconomicPolicy(mock(InternalResourceService.class));
+    }
+
+    @Test
+    public void testTrendCalculator_NoOngoingEvents() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
+
+        trendCalculator.reset(0, null);
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        trendCalculator.reset(1234, affordabilityNotes);
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendCalculator_NoAffordabilityNotes() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+
+        OngoingEvent[] events = new OngoingEvent[]{
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        null, 1, 1),
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+                        null, 2, 3),
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3",
+                        null, 3, -3),
+        };
+
+        trendCalculator.reset(0, null);
+        for (OngoingEvent event : events) {
+            trendCalculator.accept(event);
+        }
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        trendCalculator.reset(1234, affordabilityNotes);
+        for (OngoingEvent event : events) {
+            trendCalculator.accept(event);
+        }
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendCalculator_NoTrendToThreshold() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 10);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is already above threshold and events are all positive delta.
+        // There should be no time to report.
+        trendCalculator.reset(1234, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        null, 1, 1));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+                        null, 2, 3));
+
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        // Balance is already below threshold and events are all negative delta.
+        // There should be no time to report.
+        trendCalculator.reset(1, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        null, 1, -1));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+                        null, 2, -3));
+
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendCalculator_SimpleTrendToThreshold() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is below threshold and events are all positive delta.
+        // Should report the correct time to the upper threshold.
+        trendCalculator.reset(0, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+                        null, 1, 1));
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
+                        null, 2, 3));
+
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        // Balance is above the threshold and events are all negative delta.
+        // Should report the correct time to the lower threshold.
+        trendCalculator.reset(40, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        null, 1, -1));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+                        null, 2, -3));
+
+        assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendCalculator_SelectCorrectThreshold() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is below threshold and events are all positive delta.
+        // Should report the correct time to the correct upper threshold.
+        trendCalculator.reset(0, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+                        null, 1, 1));
+
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        // Balance is above the threshold and events are all negative delta.
+        // Should report the correct time to the correct lower threshold.
+        trendCalculator.reset(30, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        null, 1, -1));
+
+        assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendCalculator_TrendsToBothThresholds() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+        mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is between both thresholds and events are mixed positive/negative delta.
+        // Should report the correct time to each threshold.
+        trendCalculator.reset(35, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+                        null, 1, 3));
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
+                        null, 2, 2));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
+                        null, 3, -2));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
+                        null, 4, -3));
+
+        assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
new file mode 100644
index 0000000..65a9759
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+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;
+
+/** Test that the ledger records transactions correctly. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LedgerTest {
+
+    @Test
+    public void testInitialState() {
+        final Ledger ledger = new Ledger();
+        assertEquals(0, ledger.getCurrentBalance());
+        assertEquals(0, ledger.get24HourSum(0, 0));
+    }
+
+    @Test
+    public void testMultipleTransactions() {
+        final Ledger ledger = new Ledger();
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+        assertEquals(5, ledger.getCurrentBalance());
+        assertEquals(5, ledger.get24HourSum(1, 60_000));
+        ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+        assertEquals(30, ledger.getCurrentBalance());
+        assertEquals(30, ledger.get24HourSum(1, 60_000));
+        ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+        assertEquals(20, ledger.getCurrentBalance());
+        assertEquals(20, ledger.get24HourSum(1, 60_000));
+    }
+
+    @Test
+    public void test24HourSum() {
+        final Ledger ledger = new Ledger();
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+        assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+        ledger.recordTransaction(
+                new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+        assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+        ledger.recordTransaction(
+                new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+        assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+        assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
+        assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
+        // Pro-rated as the second transaction phases out
+        assertEquals(1251,
+                ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS));
+        assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS));
+        assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/OWNERS b/services/tests/servicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
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 8af2c4d..28838ae 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -97,7 +97,7 @@
 
         try {
             mTimeZoneDetectorService.getCapabilitiesAndConfig();
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -129,7 +129,7 @@
         ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
         try {
             mTimeZoneDetectorService.addListener(mockListener);
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -235,7 +235,7 @@
 
         try {
             mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingOrSelfPermission(
                     eq(android.Manifest.permission.SET_TIME_ZONE),
@@ -268,7 +268,7 @@
 
         try {
             mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingOrSelfPermission(
                     eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
@@ -301,7 +301,7 @@
 
         try {
             mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
@@ -317,7 +317,7 @@
 
         try {
             mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
-            fail();
+            fail("Expected SecurityException");
         } finally {
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index a0e9d97..0363625 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.timezonedetector.location;
 
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -728,6 +729,9 @@
         // Simulate the user change (but geo detection still enabled).
         testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
 
+        // Confirm that the previous suggestion was overridden.
+        mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
         // We expect the provider to end up in PROVIDER_STATE_STARTED_INITIALIZING, but it should
         // have been stopped when the user changed.
         int[] expectedStateTransitions =
@@ -1068,6 +1072,48 @@
         }
     }
 
+    @Test
+    public void destroy() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate the primary provider suggesting a time zone.
+        mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+        // Receiving a "success" provider event should cause a suggestion to be made synchronously,
+        // and also clear the scheduled uncertainty suggestion.
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Trigger destroy().
+        controllerImpl.destroy();
+
+        // Confirm that the previous suggestion was overridden.
+        mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+                PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+                PROVIDER_STATE_DESTROYED);
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+    }
+
     private static void assertUncertaintyTimeoutSet(
             LocationTimeZoneProviderController.Environment environment,
             LocationTimeZoneProviderController controller) {
@@ -1175,7 +1221,6 @@
         private final TestState<ProviderState> mTestProviderState = new TestState<>();
         private boolean mFailDuringInitialization;
         private boolean mInitialized;
-        private boolean mDestroyed;
 
         /**
          * Creates the instance.
@@ -1200,7 +1245,7 @@
 
         @Override
         void onDestroy() {
-            mDestroyed = true;
+            // No behavior needed.
         }
 
         @Override
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 aadab6e..2f36c7f 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
@@ -365,13 +365,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -415,13 +415,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -511,13 +511,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -567,13 +567,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init cicam/cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -697,13 +697,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init lnb resources.
         int[] lnbHandles = {1};
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index b11c85c0..c611e38 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -74,3 +74,17 @@
     resource_dirs: ["res"],
     manifest: "AndroidManifestApp4.xml",
 }
+
+android_test_helper_app {
+    name: "PackageParserTestApp5",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    resource_dirs: ["res"],
+    manifest: "AndroidManifestApp5.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
new file mode 100644
index 0000000..cc6caad
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.servicestests.apps.packageparserapp" >
+
+    <uses-sdk android:minSdkVersion="3"
+              android:targetSdkVersion="3" />
+
+    <application>
+        <activity android:name=".TestActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index fd68046..0169a7d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -16,10 +16,13 @@
 
 package com.android.server.notification;
 
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -116,4 +119,15 @@
                 SmallHash.hash(group.hashCode()),
                 p.getGroupIdHash());
     }
+
+    @Test
+    public void testIsForegroundService() {
+        NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+                0, null);
+        assertFalse(NotificationRecordLogger.isForegroundService(p.r));
+
+        // Set foreground service
+        p.r.getSbn().getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        assertTrue(NotificationRecordLogger.isForegroundService(p.r));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 1116204..9ad007d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -36,6 +36,7 @@
 
 import java.util.Calendar;
 import java.util.GregorianCalendar;
+import java.util.TimeZone;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -171,6 +172,119 @@
     }
 
     @Test
+    public void testGetNextChangeTime_startTomorrowInDaylight() {
+        // Test that the correct thing happens when the next start time would be tomorrow, during
+        // a schedule start time that doesn't exist that day. Consistent with "start times" as
+        // implemented in isInSchedule, this should get adjusted to the closest actual time.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // "today" = the day before the skipped hour for daylight savings.
+        Calendar today = getDaylightSavingsForwardDay();
+        today.set(Calendar.HOUR_OF_DAY, 23);
+        today.set(Calendar.MINUTE, 15);
+        Calendar tomorrow = getDaylightSavingsForwardDay();
+        tomorrow.add(Calendar.DATE, 1);
+        mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+                tomorrow.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 2;
+        mScheduleInfo.endHour = 4;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // The expected next change time should be tomorrow, 3AM as 2:15AM doesn't exist.
+        Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        expected.setTimeInMillis(tomorrow.getTimeInMillis());
+        expected.set(Calendar.HOUR_OF_DAY, 3);
+        expected.set(Calendar.MINUTE, 0);
+        expected.set(Calendar.SECOND, 0);
+        expected.set(Calendar.MILLISECOND, 0);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylight() {
+        // Test that the correct thing happens when the next start time would be tomorrow, but
+        // today is the day when daylight time switches over (so the "schedule start time" today
+        // may not exist).
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // "today" = the day with the skipped hour for daylight savings.
+        Calendar today = getDaylightSavingsForwardDay();
+        today.add(Calendar.DATE, 1);
+        today.set(Calendar.HOUR_OF_DAY, 23);
+        today.set(Calendar.MINUTE, 15);
+        Calendar tomorrow = getDaylightSavingsForwardDay();
+        tomorrow.add(Calendar.DATE, 2);
+        mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+                tomorrow.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 2;
+        mScheduleInfo.endHour = 4;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // The expected next change time should be tomorrow, 2:15AM.
+        Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        expected.setTimeInMillis(tomorrow.getTimeInMillis());
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+        expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+        expected.set(Calendar.SECOND, 0);
+        expected.set(Calendar.MILLISECOND, 0);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylightBackward() {
+        // Test that the correct thing happens when the next start time would be tomorrow, but
+        // today is the day when clocks are adjusted backwards (so the "schedule start time" today
+        // exists twice).
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // "today" = the day with the extra hour for daylight savings.
+        Calendar today = getDaylightSavingsBackwardDay();
+        today.add(Calendar.DATE, 1);
+        today.set(Calendar.HOUR_OF_DAY, 23);
+        today.set(Calendar.MINUTE, 15);
+        Calendar tomorrow = getDaylightSavingsBackwardDay();
+        tomorrow.add(Calendar.DATE, 2);
+        mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+                tomorrow.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 1;
+        mScheduleInfo.endHour = 4;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // The expected next change time should be tomorrow, 1:15AM.
+        Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        expected.setTimeInMillis(tomorrow.getTimeInMillis());
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+        expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+        expected.set(Calendar.SECOND, 0);
+        expected.set(Calendar.MILLISECOND, 0);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
     public void testShouldExitForAlarm_settingOff() {
         mScheduleInfo.exitAtAlarm = false;
         mScheduleInfo.nextAlarm = 1000;
@@ -416,22 +530,264 @@
     }
 
     @Test
+    public void testIsInSchedule_daylightSavingsForward_startDuringChange() {
+        // Test that if the start time of a ScheduleCalendar is during the nonexistent
+        // hour of daylight savings forward time, the evaluation of whether a time is in the
+        // schedule still works.
+
+        // Set timezone to make sure we're evaluating the correct days.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // Set up schedule for 2:30AM - 4:00AM.
+        final Calendar dstYesterday = getDaylightSavingsForwardDay();
+        final Calendar dstToday = getDaylightSavingsForwardDay();
+        dstToday.add(Calendar.DATE, 1);
+        mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+                dstToday.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 2;
+        mScheduleInfo.startMinute = 30;
+        mScheduleInfo.endHour = 4;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // Test cases: there are 2 "on" periods. These cover: before the first schedule
+        // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+        // (one on each calendar day), during the second (3:30AM), and after the second (4:30AM)
+        Calendar out1 = getDaylightSavingsForwardDay();
+        out1.set(Calendar.HOUR_OF_DAY, 1);
+        out1.set(Calendar.MINUTE, 00);
+        out1.set(Calendar.SECOND, 0);
+        out1.set(Calendar.MILLISECOND, 0);
+
+        Calendar in1 = getDaylightSavingsForwardDay();
+        in1.set(Calendar.HOUR_OF_DAY, 2);
+        in1.set(Calendar.MINUTE, 45);
+        in1.set(Calendar.SECOND, 0);
+        in1.set(Calendar.MILLISECOND, 0);
+
+        Calendar midOut1 = getDaylightSavingsForwardDay();
+        midOut1.set(Calendar.HOUR_OF_DAY, 7);
+        midOut1.set(Calendar.MINUTE, 30);
+        midOut1.set(Calendar.SECOND, 0);
+        midOut1.set(Calendar.MILLISECOND, 0);
+
+        Calendar midOut2 = getDaylightSavingsForwardDay();
+        midOut2.add(Calendar.DATE, 1);
+        midOut2.set(Calendar.HOUR_OF_DAY, 1);
+        midOut2.set(Calendar.MINUTE, 30);
+        midOut2.set(Calendar.SECOND, 0);
+        midOut2.set(Calendar.MILLISECOND, 0);
+
+        // Question: should 3:15AM be in the 2:30-4 schedule on a day when 2:30-3 doesn't exist?
+        Calendar in2 = getDaylightSavingsForwardDay();
+        in2.add(Calendar.DATE, 1);
+        in2.set(Calendar.HOUR_OF_DAY, 3);
+        in2.set(Calendar.MINUTE, 30);
+        in2.set(Calendar.SECOND, 0);
+        in2.set(Calendar.MILLISECOND, 0);
+
+        Calendar out2 = getDaylightSavingsForwardDay();
+        out2.add(Calendar.DATE, 1);
+        out2.set(Calendar.HOUR_OF_DAY, 4);
+        out2.set(Calendar.MINUTE, 30);
+        out2.set(Calendar.SECOND, 0);
+        out2.set(Calendar.MILLISECOND, 0);
+
+        assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_daylightSavingsForward_endDuringChange() {
+        // Test that if the end time of a ScheduleCalendar is during the nonexistent
+        // hour of daylight savings forward time, the evaluation of whether a time is in the
+        // schedule still works.
+
+        // Set timezone to make sure we're evaluating the correct days.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // Set up schedule for 11:00PM - 2:30AM. On the day when 2AM doesn't exist, this should
+        // effectively finish at 3:30AM(?)
+        final Calendar dstYesterday = getDaylightSavingsForwardDay();
+        final Calendar dstToday = getDaylightSavingsForwardDay();
+        dstToday.add(Calendar.DATE, 1);
+        mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+                dstToday.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 23;
+        mScheduleInfo.endHour = 2;
+        mScheduleInfo.endMinute = 30;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // Test cases: before the time period on the previous day; during the time period when
+        // the calendar day is still the previous day; during the time period when the calendar
+        // day is the change day; afterwards.
+        Calendar out1 = getDaylightSavingsForwardDay();
+        out1.set(Calendar.HOUR_OF_DAY, 22);
+        out1.set(Calendar.MINUTE, 00);
+        out1.set(Calendar.SECOND, 0);
+        out1.set(Calendar.MILLISECOND, 0);
+
+        Calendar in1 = getDaylightSavingsForwardDay();
+        in1.set(Calendar.HOUR_OF_DAY, 23);
+        in1.set(Calendar.MINUTE, 30);
+        in1.set(Calendar.SECOND, 0);
+        in1.set(Calendar.MILLISECOND, 0);
+
+        Calendar in2 = getDaylightSavingsForwardDay();
+        in2.add(Calendar.DATE, 1);
+        in2.set(Calendar.HOUR_OF_DAY, 1);
+        in2.set(Calendar.MINUTE, 30);
+        in2.set(Calendar.SECOND, 0);
+        in2.set(Calendar.MILLISECOND, 0);
+
+        // Question: Should 3:15AM be out of the schedule on a day when 2-3 doesn't exist?
+        Calendar out2 = getDaylightSavingsForwardDay();
+        out2.add(Calendar.DATE, 1);
+        out2.set(Calendar.HOUR_OF_DAY, 3);
+        out2.set(Calendar.MINUTE, 45);
+        out2.set(Calendar.SECOND, 0);
+        out2.set(Calendar.MILLISECOND, 0);
+
+        assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_daylightSavingsBackward_startDuringChange() {
+        // Test that if the start time of a ScheduleCalendar is during the duplicated
+        // hour of daylight savings backward time, the evaluation of whether a time is in the
+        // schedule still works. It's not clear what correct behavior is during the duplicated
+        // 1:00->1:59->1:00->1:59 time period, but times outside that should still work.
+
+        // Set timezone to make sure we're evaluating the correct days.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // Set up schedule for 1:15AM - 4:00AM.
+        final Calendar dstYesterday = getDaylightSavingsBackwardDay();
+        final Calendar dstToday = getDaylightSavingsBackwardDay();
+        dstToday.add(Calendar.DATE, 1);
+        mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+                dstToday.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 1;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endHour = 4;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // Test cases: there are 2 "on" periods. These cover: before the first schedule
+        // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+        // (one on each calendar day), during the second (2:30AM), and after the second (4:30AM)
+        Calendar out1 = getDaylightSavingsBackwardDay();
+        out1.set(Calendar.HOUR_OF_DAY, 1);
+        out1.set(Calendar.MINUTE, 00);
+        out1.set(Calendar.SECOND, 0);
+        out1.set(Calendar.MILLISECOND, 0);
+
+        Calendar in1 = getDaylightSavingsBackwardDay();
+        in1.set(Calendar.HOUR_OF_DAY, 2);
+        in1.set(Calendar.MINUTE, 30);
+        in1.set(Calendar.SECOND, 0);
+        in1.set(Calendar.MILLISECOND, 0);
+
+        Calendar midOut1 = getDaylightSavingsBackwardDay();
+        midOut1.set(Calendar.HOUR_OF_DAY, 7);
+        midOut1.set(Calendar.MINUTE, 30);
+        midOut1.set(Calendar.SECOND, 0);
+        midOut1.set(Calendar.MILLISECOND, 0);
+
+        Calendar midOut2 = getDaylightSavingsBackwardDay();
+        midOut2.add(Calendar.DATE, 1);
+        midOut2.set(Calendar.HOUR_OF_DAY, 0);
+        midOut2.set(Calendar.MINUTE, 30);
+        midOut2.set(Calendar.SECOND, 0);
+        midOut2.set(Calendar.MILLISECOND, 0);
+
+        Calendar in2 = getDaylightSavingsBackwardDay();
+        in2.add(Calendar.DATE, 1);
+        in2.set(Calendar.HOUR_OF_DAY, 2);
+        in2.set(Calendar.MINUTE, 30);
+        in2.set(Calendar.SECOND, 0);
+        in2.set(Calendar.MILLISECOND, 0);
+
+        Calendar out2 = getDaylightSavingsBackwardDay();
+        out2.add(Calendar.DATE, 1);
+        out2.set(Calendar.HOUR_OF_DAY, 4);
+        out2.set(Calendar.MINUTE, 30);
+        out2.set(Calendar.SECOND, 0);
+        out2.set(Calendar.MILLISECOND, 0);
+
+        assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+        assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_daylightSavings_flippedSchedule() {
+        // This test is for the unlikely edge case where the skipped hour due to daylight savings
+        // causes the evaluated start time to be "later" than the schedule's end time on that day,
+        // for instance if the schedule is 2:30AM-3:15AM; 2:30AM may evaluate to 3:30AM on the day
+        // of daylight change.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+        // Set up schedule for 2:30AM - 3:15AM.
+        final Calendar dstYesterday = getDaylightSavingsForwardDay();
+        final Calendar dstToday = getDaylightSavingsForwardDay();
+        dstToday.add(Calendar.DATE, 1);
+        mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+                dstToday.get(Calendar.DAY_OF_WEEK)};
+        mScheduleInfo.startHour = 2;
+        mScheduleInfo.startMinute = 30;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.endMinute = 15;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        // It may not be well-defined what times around the 2-3AM range one might expect to be
+        // included or not included on the weird day when 2AM doesn't exist, but other unrelated
+        // times of day (here, 3PM) should definitely be out.
+        Calendar out1 = getDaylightSavingsForwardDay();
+        out1.set(Calendar.HOUR_OF_DAY, 15);
+        out1.set(Calendar.MINUTE, 0);
+        out1.set(Calendar.SECOND, 0);
+        out1.set(Calendar.MILLISECOND, 0);
+
+        Calendar out2 = getDaylightSavingsForwardDay();
+        out2.add(Calendar.DATE, 1);
+        out2.set(Calendar.HOUR_OF_DAY, 15);
+        out2.set(Calendar.MINUTE, 0);
+        out2.set(Calendar.SECOND, 0);
+        out2.set(Calendar.MILLISECOND, 0);
+
+        assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+        assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+    }
+
+    @Test
     public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger_daylightSavings() {
-        Calendar alarm = getDaylightSavingsDay();
+        // Need to set the time zone explicitly to a US one so that the daylight savings time day is
+        // correct.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+        Calendar alarm = getDaylightSavingsForwardDay();
         alarm.set(Calendar.HOUR_OF_DAY, 23);
         alarm.set(Calendar.MINUTE, 15);
         alarm.set(Calendar.SECOND, 0);
         alarm.set(Calendar.MILLISECOND, 0);
 
-        Calendar now = getDaylightSavingsDay();
+        Calendar now = getDaylightSavingsForwardDay();
         now.set(Calendar.HOUR_OF_DAY, 2);
         now.set(Calendar.MINUTE, 10);
         now.set(Calendar.SECOND, 0);
         now.set(Calendar.MILLISECOND, 0);
         now.add(Calendar.DATE, 1); // add a day, on daylight savings this becomes 3:10am
 
-        final Calendar tempToday = getDaylightSavingsDay();
-        final Calendar tempTomorrow = getDaylightSavingsDay();
+        final Calendar tempToday = getDaylightSavingsForwardDay();
+        final Calendar tempTomorrow = getDaylightSavingsForwardDay();
         tempTomorrow.add(Calendar.DATE, 1);
         mScheduleInfo.days = new int[] {tempToday.get(Calendar.DAY_OF_WEEK),
                 tempTomorrow.get(Calendar.DAY_OF_WEEK)};
@@ -506,6 +862,80 @@
                 now.getTimeInMillis()));
     }
 
+    @Test
+    public void testClosestActualTime_regularTimesAndSkippedTime() {
+        // Make sure we're operating in the relevant time zone for the assumed Daylight Savings day
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+        Calendar day = getDaylightSavingsForwardDay();
+        day.set(Calendar.HOUR_OF_DAY, 15);
+        day.set(Calendar.MINUTE, 25);
+        day.set(Calendar.SECOND, 0);
+        day.set(Calendar.MILLISECOND, 0);
+        assertEquals(day.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+
+        // Check a skipped time
+        day.add(Calendar.DATE, 1);
+        day.set(Calendar.HOUR_OF_DAY, 3);
+        day.set(Calendar.MINUTE, 0);
+        day.set(Calendar.SECOND, 0);
+        day.set(Calendar.MILLISECOND, 0);
+        assertEquals(day.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 2, 15));
+
+        // Check a non-skipped time after the clocks have moved forward
+        day.set(Calendar.HOUR_OF_DAY, 15);
+        day.set(Calendar.MINUTE, 25);
+        day.set(Calendar.SECOND, 0);
+        day.set(Calendar.MILLISECOND, 0);
+        assertEquals(day.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+    }
+
+    @Test
+    public void testClosestActualTime_otherTimeZones() {
+        // Make sure this doesn't only work for US/Eastern time.
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/London"));
+        Calendar ukDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/London"));
+        ukDstDay.set(2021, Calendar.MARCH, 28);
+
+        // Check a skipped time, which is 01:xx on that day in the UK
+        ukDstDay.set(Calendar.HOUR_OF_DAY, 2);
+        ukDstDay.set(Calendar.MINUTE, 0);
+        ukDstDay.set(Calendar.SECOND, 0);
+        ukDstDay.set(Calendar.MILLISECOND, 0);
+        assertEquals(ukDstDay.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 1, 25));
+
+        // Check a non-skipped time
+        ukDstDay.set(Calendar.HOUR_OF_DAY, 11);
+        ukDstDay.set(Calendar.MINUTE, 23);
+        ukDstDay.set(Calendar.SECOND, 0);
+        ukDstDay.set(Calendar.MILLISECOND, 0);
+        assertEquals(ukDstDay.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 11, 23));
+
+        mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
+        Calendar frDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris"));
+        frDstDay.set(2021, Calendar.MARCH, 28);
+
+        // Check a skipped time, which is 02:xx on that day in France
+        frDstDay.set(Calendar.HOUR_OF_DAY, 3);
+        frDstDay.set(Calendar.MINUTE, 0);
+        frDstDay.set(Calendar.SECOND, 0);
+        frDstDay.set(Calendar.MILLISECOND, 0);
+        assertEquals(frDstDay.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 2, 25));
+
+        // Check a regular time
+        frDstDay.set(Calendar.HOUR_OF_DAY, 14);
+        frDstDay.set(Calendar.MINUTE, 59);
+        frDstDay.set(Calendar.SECOND, 0);
+        frDstDay.set(Calendar.MILLISECOND, 0);
+        assertEquals(frDstDay.getTimeInMillis(),
+                mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 14, 59));
+    }
+
     private int getTodayDay() {
         return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
     }
@@ -517,9 +947,23 @@
     }
 
 
-    private Calendar getDaylightSavingsDay() {
-        // the day before daylight savings in the US - March 9, 2019
-        Calendar daylightSavingsDay = new GregorianCalendar(2019, 2, 9);
+    private Calendar getDaylightSavingsForwardDay() {
+        // the day before daylight savings rolls forward in the US - March 9, 2019
+        // 2AM March 10, 2019 does not exist -- goes straight from 1:59 to 3:00
+        // Specifically set to US/Eastern time zone rather than relying on a default time zone
+        // to make sure the date is the correct one, since DST changes vary by region.
+        Calendar daylightSavingsDay = new GregorianCalendar(
+                TimeZone.getTimeZone("America/New_York"));
+        daylightSavingsDay.set(2019, Calendar.MARCH, 9);
+        return daylightSavingsDay;
+    }
+
+    private Calendar getDaylightSavingsBackwardDay() {
+        // the day before daylight savings rolls backward in the US - November 2, 2019
+        // In this instance, 1AM November 3 2019 is repeated twice; 1:00->1:59->1:00->1:59->2:00
+        Calendar daylightSavingsDay = new GregorianCalendar(
+                TimeZone.getTimeZone("America/New_York"));
+        daylightSavingsDay.set(2019, Calendar.NOVEMBER, 2);
         return daylightSavingsDay;
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 4410404..31be33e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -28,6 +28,7 @@
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -1263,6 +1264,7 @@
 
         assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
                         | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_AMBIENT
                         | SUPPRESSED_EFFECT_PEEK,
                 mZenModeHelperSpy.mConfig.suppressedVisualEffects);
 
@@ -1296,7 +1298,9 @@
         parser.nextTag();
         mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS,
+        assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+                        | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_AMBIENT,
                 mZenModeHelperSpy.mConfig.suppressedVisualEffects);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 32a4774..fa1f4ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -44,6 +44,7 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
+import android.window.WindowContainerToken;
 
 import androidx.test.filters.SmallTest;
 
@@ -260,7 +261,7 @@
         notifyActivityLaunching(mTopActivity.intent);
         notifyActivityLaunched(START_SUCCESS, mTopActivity);
         doReturn(true).when(mTopActivity.mDisplayContent).isSleeping();
-        mTopActivity.setState(Task.ActivityState.RESUMED, "test");
+        mTopActivity.setState(ActivityRecord.State.RESUMED, "test");
         mTopActivity.setVisibility(false);
         waitHandlerIdle(mAtm.mH);
         // Not cancel immediately because in one of real cases, the keyguard may be going away or
@@ -449,8 +450,10 @@
     @Test
     public void testConsecutiveLaunchNewTask() {
         final IBinder launchCookie = mock(IBinder.class);
+        final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
         mTrampolineActivity.noDisplay = true;
         mTrampolineActivity.mLaunchCookie = launchCookie;
+        mTrampolineActivity.mLaunchRootTask = launchRootTask;
         onActivityLaunched(mTrampolineActivity);
         final ActivityRecord activityOnNewTask = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
@@ -464,6 +467,10 @@
                 mTrampolineActivity.mLaunchCookie).isNull();
         assertWithMessage("The last launch task has the transferred cookie").that(
                 activityOnNewTask.mLaunchCookie).isEqualTo(launchCookie);
+        assertWithMessage("Trampoline's launch root task must be transferred").that(
+                mTrampolineActivity.mLaunchRootTask).isNull();
+        assertWithMessage("The last launch task has the transferred launch root task").that(
+                activityOnNewTask.mLaunchRootTask).isEqualTo(launchRootTask);
     }
 
     @Test
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 293e862a..e367579 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -66,19 +66,19 @@
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-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;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -136,7 +136,7 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.R;
-import com.android.server.wm.Task.ActivityState;
+import com.android.server.wm.ActivityRecord.State;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -171,25 +171,25 @@
     }
 
     @Test
-    public void testStackCleanupOnClearingTask() {
+    public void testTaskFragmentCleanupOnClearingTask() {
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task task = activity.getTask();
-        final Task rootTask = activity.getRootTask();
+        final TaskFragment taskFragment = activity.getTaskFragment();
         activity.onParentChanged(null /*newParent*/, task);
-        verify(rootTask, times(1)).cleanUpActivityReferences(any());
+        verify(taskFragment).cleanUpActivityReferences(any());
     }
 
     @Test
-    public void testStackCleanupOnActivityRemoval() {
+    public void testTaskFragmentCleanupOnActivityRemoval() {
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task task = activity.getTask();
-        final Task rootTask = activity.getRootTask();
+        final TaskFragment taskFragment = activity.getTaskFragment();
         task.removeChild(activity);
-        verify(rootTask, times(1)).cleanUpActivityReferences(any());
+        verify(taskFragment).cleanUpActivityReferences(any());
     }
 
     @Test
-    public void testStackCleanupOnTaskRemoval() {
+    public void testRootTaskCleanupOnTaskRemoval() {
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task task = activity.getTask();
         final Task rootTask = activity.getRootTask();
@@ -344,7 +344,7 @@
     public void testSetsRelaunchReason_NotDragResizing() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
         activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -369,7 +369,7 @@
     public void testSetsRelaunchReason_DragResizing() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
         activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -396,7 +396,7 @@
     public void testRelaunchClearTopWaitingTranslucent() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
         activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -417,7 +417,7 @@
     public void testSetsRelaunchReason_NonResizeConfigChanges() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(RESUMED, "Testing");
 
         task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
         activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -465,7 +465,7 @@
                 .setCreateTask(true)
                 .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
                 .build();
-        activity.setState(Task.ActivityState.RESUMED, "Testing");
+        activity.setState(RESUMED, "Testing");
 
         activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 activity.getConfiguration()));
@@ -628,7 +628,7 @@
     @Test
     public void testShouldMakeActive_deferredResume() {
         final ActivityRecord activity = createActivityWithTask();
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
 
         mSupervisor.beginDeferResume();
         assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
@@ -644,7 +644,7 @@
         ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
         finishingActivity.finishing = true;
         ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
 
         assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
     }
@@ -653,15 +653,16 @@
     public void testShouldResume_stackVisibility() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
 
-        doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+        doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
         assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
 
-        doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+        doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT)
+                .when(task).getVisibility(null);
         assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
 
-        doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+        doReturn(TASK_FRAGMENT_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
         assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
     }
 
@@ -669,13 +670,13 @@
     public void testShouldResumeOrPauseWithResults() {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
 
         ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
         topActivity.finishing = true;
 
-        doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+        doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
         assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
         assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
     }
@@ -688,7 +689,7 @@
                 .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
                 .build();
         final Task task = activity.getTask();
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
 
         final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         try {
@@ -731,7 +732,7 @@
         final ActivityRecord activity = createActivityWithTask();
         ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
         topActivity.setOccludesParent(false);
-        activity.setState(Task.ActivityState.STOPPED, "Testing");
+        activity.setState(STOPPED, "Testing");
         activity.setVisibility(true);
         activity.makeActiveIfNeeded(null /* activeActivity */);
         assertEquals(STARTED, activity.getState());
@@ -1015,8 +1016,8 @@
     @Test
     public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
         final ActivityRecord activity = createActivityWithTask();
-        final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
-        for (ActivityState state : states) {
+        final State[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+        for (State state : states) {
             activity.finishing = false;
             activity.setState(state, "test");
             reset(activity);
@@ -1151,7 +1152,7 @@
     /**
      * Verify that finish request won't change the state of next top activity if the current
      * finishing activity doesn't need to be destroyed immediately. The case is usually like
-     * from {@link ActivityStack#completePauseLocked(boolean, ActivityRecord)} to
+     * from {@link Task#completePause(boolean, ActivityRecord)} to
      * {@link ActivityRecord#completeFinishing(String)}, so the complete-pause should take the
      * responsibility to resume the next activity with updating the state.
      */
@@ -1397,7 +1398,7 @@
     }
 
     private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
-            ActivityState secondActivityState) {
+            State secondActivityState) {
         final ActivityRecord activity = createActivityWithTask();
         final Task task = activity.getTask();
         final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1449,7 +1450,8 @@
     @Test
     public void testDestroyIfPossible() {
         final ActivityRecord activity = createActivityWithTask();
-        doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+        doReturn(false).when(mRootWindowContainer)
+                .resumeFocusedTasksTopActivities();
         activity.destroyIfPossible("test");
 
         assertEquals(DESTROYING, activity.getState());
@@ -1471,7 +1473,8 @@
             homeStack.removeChild(t, "test");
         }, true /* traverseTopToBottom */);
         activity.finishing = true;
-        doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+        doReturn(false).when(mRootWindowContainer)
+                .resumeFocusedTasksTopActivities();
 
         // Try to destroy the last activity above the home stack.
         activity.destroyIfPossible("test");
@@ -1907,8 +1910,7 @@
         assertTrue(wpc.registeredForActivityConfigChanges());
 
         // Create a new task with custom config to reparent the activity to.
-        final Task newTask =
-                new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+        final Task newTask = new TaskBuilder(mSupervisor).build();
         final Configuration newConfig = newTask.getConfiguration();
         newConfig.densityDpi += 100;
         newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1940,8 +1942,7 @@
                 .diff(wpc.getRequestedOverrideConfiguration()));
 
         // Create a new task with custom config to reparent the second activity to.
-        final Task newTask =
-                new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+        final Task newTask = new TaskBuilder(mSupervisor).build();
         final Configuration newConfig = newTask.getConfiguration();
         newConfig.densityDpi += 100;
         newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -2151,7 +2152,7 @@
         assertFalse(activity.supportsPictureInPicture());
     }
 
-    private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+    private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
             boolean shouldUpdate, boolean activityChange) {
         reset(activity.app);
         activity.setState(state, "test");
@@ -2589,13 +2590,13 @@
                 false /* activityCreate */, false /* suggestEmpty */);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
-        activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
 
         doCallRealMethod().when(task).startActivityLocked(
                 any(), any(), anyBoolean(), anyBoolean(), any(), any());
         // In normal case, resumeFocusedTasksTopActivities() should be called after
         // startActivityLocked(). So skip resumeFocusedTasksTopActivities() in ActivityBuilder.
-        doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+        doReturn(false).when(mRootWindowContainer)
+                .resumeFocusedTasksTopActivities();
         // Make mVisibleSetFromTransferredStartingWindow true.
         final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
         task.startActivityLocked(middle, null /* focusedTopActivity */,
@@ -2738,6 +2739,40 @@
     }
 
     @Test
+    public void testCloseToSquareFixedOrientationPortrait() {
+        // create a square display
+        final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+                .setSystemDecorations(true).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+        // create a fixed portrait activity
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+
+        // check that both the configuration and app bounds are portrait
+        assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+        assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+                <= activity.getConfiguration().windowConfiguration.getAppBounds().height());
+    }
+
+    @Test
+    public void testCloseToSquareFixedOrientationLandscape() {
+        // create a square display
+        final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+                .setSystemDecorations(true).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+        // create a fixed landscape activity
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build();
+
+        // check that both the configuration and app bounds are landscape
+        assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+        assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+                > activity.getConfiguration().windowConfiguration.getAppBounds().height());
+    }
+
+    @Test
     public void testSetVisibility_visibleToVisible() {
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index d0588a3..2df9a8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1136,6 +1136,7 @@
                 /* doResume */true,
                 /* options */null,
                 /* inTask */null,
+                /* inTaskFragment */ null,
                 /* restrictedBgActivity */false,
                 /* intentGrants */null);
 
@@ -1146,6 +1147,31 @@
     }
 
     @Test
+    public void testStartActivityInner_inTaskFragment() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertTrue(taskFragment.hasChild());
+    }
+
+    @Test
     public void testLaunchCookie_newAndExistingTask() {
         final ActivityStarter starter = prepareStarter(0, false);
 
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 741f33f..8ca14bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,10 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -244,7 +248,7 @@
                 .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
                 .build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        activity.setState(Task.ActivityState.RESUMED, "test");
+        activity.setState(RESUMED, "test");
         mSupervisor.endDeferResume();
 
         assertEquals(activity.app, mAtm.mInternal.getTopApp());
@@ -254,13 +258,13 @@
         activity.mVisibleRequested = false;
         activity.setVisible(false);
         activity.getTask().setPausingActivity(activity);
-        homeActivity.setState(Task.ActivityState.PAUSED, "test");
+        homeActivity.setState(PAUSED, "test");
 
         // Even the visibility states are invisible, the next activity should be resumed because
         // the crashed activity was pausing.
         mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
                 null /* finishInstrumentationCallback */);
-        assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+        assertEquals(RESUMED, homeActivity.getState());
         assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
     }
 
@@ -271,7 +275,7 @@
         final Task rootHomeTask = mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
         final ActivityRecord homeActivity = new ActivityBuilder(mAtm).setTask(rootHomeTask).build();
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        topActivity.setState(Task.ActivityState.RESUMED, "test");
+        topActivity.setState(RESUMED, "test");
 
         final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
             assertFalse(mAtm.mInternal.isSleeping());
@@ -287,7 +291,7 @@
         verify(mSupervisor.mGoingToSleepWakeLock).acquire();
         doReturn(true).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
 
-        assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+        assertEquals(PAUSING, topActivity.getState());
         assertTrue(mAtm.mInternal.isSleeping());
         assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
                 mAtm.mInternal.getTopProcessState());
@@ -298,7 +302,7 @@
         final Task topRootTask = topActivity.getRootTask();
         doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
         doReturn(true).when(topRootTask).goToSleepIfPossible(anyBoolean());
-        topActivity.setState(Task.ActivityState.STOPPING, "test");
+        topActivity.setState(STOPPING, "test");
         topActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */,
                 null /* description */);
         verify(mSupervisor.mGoingToSleepWakeLock).release();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 31d4612..af21e02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -35,10 +35,10 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
-import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 
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 4e4e0ed..1f123cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -63,6 +63,7 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
+import android.window.DisplayAreaInfo;
 import android.window.IDisplayAreaOrganizer;
 
 import com.google.android.collect.Lists;
@@ -570,6 +571,31 @@
     }
 
     @Test
+    public void testGetDisplayAreaInfo() {
+        final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+                mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+        mDisplayContent.addChild(displayArea, 0);
+        final DisplayAreaInfo info = displayArea.getDisplayAreaInfo();
+
+        assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken());
+        assertThat(info.configuration).isEqualTo(displayArea.getConfiguration());
+        assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId());
+        assertThat(info.featureId).isEqualTo(displayArea.mFeatureId);
+        assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId);
+
+        final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+        final int tdaIndex = tda.getParent().mChildren.indexOf(tda);
+        final RootDisplayArea root =
+                new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1);
+        mDisplayContent.addChild(root, tdaIndex + 1);
+        displayArea.reparent(root, 0);
+
+        final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo();
+
+        assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId);
+    }
+
+    @Test
     public void testRegisterSameFeatureOrganizer_expectThrowsException() {
         final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
         final IBinder binder = mock(IBinder.class);
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 d498d46..a0b5fed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -590,7 +590,9 @@
         dc.setImeLayeringTarget(ws);
 
         // Adjust bounds so that matchesRootDisplayAreaBounds() returns false.
-        ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1));
+        final Rect bounds = new Rect(dc.getBounds());
+        bounds.scale(0.5f);
+        ws.mActivityRecord.setBounds(bounds);
         assertFalse("matchesRootDisplayAreaBounds() should return false",
                 ws.matchesDisplayAreaBounds());
 
@@ -1528,7 +1530,7 @@
         unblockDisplayRotation(mDisplayContent);
         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
         app.setVisible(false);
-        app.setState(Task.ActivityState.RESUMED, "test");
+        app.setState(ActivityRecord.State.RESUMED, "test");
         mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
         mDisplayContent.mOpeningApps.add(app);
         final int newOrientation = getRotatedOrientation(mDisplayContent);
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 03304bb..3741d49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -24,7 +24,7 @@
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
@@ -50,13 +50,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 import static org.testng.Assert.expectThrows;
 
 import android.graphics.Insets;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
@@ -69,7 +69,6 @@
 import android.view.RoundedCorners;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Type;
-import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -109,12 +108,7 @@
         mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
         // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
         // changing those frames.
-        doNothing().when(mWindow).computeFrame();
-
-        final WindowManager.LayoutParams attrs = mWindow.mAttrs;
-        attrs.width = MATCH_PARENT;
-        attrs.height = MATCH_PARENT;
-        attrs.format = PixelFormat.TRANSLUCENT;
+        doNothing().when(mWindow).computeFrame(any());
 
         spyOn(mStatusBarWindow);
         spyOn(mNavBarWindow);
@@ -219,7 +213,7 @@
     }
 
     @Test
-    public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+    public void addingWindow_InWindowTypeWithPredefinedInsets() {
         mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
         WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
         win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
@@ -230,7 +224,13 @@
 
         InsetsSourceProvider provider =
                 mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
-        assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            // In the new flexible insets setup, the insets frame should always respect the window
+            // layout result.
+            assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+        } else {
+            assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+        }
     }
 
     @Test
@@ -733,10 +733,12 @@
 
     @Test
     public void testFixedRotationInsetsSourceFrame() {
+        mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT;
+        mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH;
         doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
-        mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
-                .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
+        mWindow.mAboveInsetsState.set(
+                mDisplayContent.getInsetsStateController().getRawInsetsState());
         final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
                 .getSource(ITYPE_STATUS_BAR).getFrame();
         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
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 b793be7..70aa2a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -37,7 +37,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
 
 import static org.junit.Assert.assertEquals;
@@ -107,8 +106,7 @@
 
     @Test
     public void testChooseNavigationColorWindowLw() {
-        final WindowState opaque = createOpaqueFullscreen(false);
-
+        final WindowState candidate = createOpaqueFullscreen(false);
         final WindowState dimmingImTarget = createDimmingDialogWindow(true);
         final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
 
@@ -116,45 +114,51 @@
         final WindowState invisibleIme = createInputMethodWindow(false, true, false);
         final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
 
-        // If everything is null, return null
+        // If everything is null, return null.
         assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
-                null, null, null, NAV_BAR_BOTTOM));
+                null, null, NAV_BAR_BOTTOM));
 
-        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, null, NAV_BAR_BOTTOM));
+        // If no IME windows, return candidate window.
+        assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+                candidate, null, NAV_BAR_BOTTOM));
         assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+                dimmingImTarget, null, NAV_BAR_BOTTOM));
         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+                dimmingNonImTarget, null, NAV_BAR_BOTTOM));
 
-        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
-                null, null, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
-                null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        // If IME is not visible, return candidate window.
+        assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+                candidate, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                dimmingImTarget, invisibleIme, NAV_BAR_BOTTOM));
         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+                dimmingNonImTarget, invisibleIme, NAV_BAR_BOTTOM));
 
-        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+        // If IME is visible, return candidate when the candidate window is not dimming.
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                candidate, visibleIme, NAV_BAR_BOTTOM));
+
+        // If IME is visible and the candidate window is dimming, checks whether the dimming window
+        // can be IME tartget or not.
+        assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+                dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+                dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
 
         // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
         // window.
-        assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+                null, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+                candidate, imeNonDrawNavBar, NAV_BAR_BOTTOM));
         assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+                dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+                dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
     }
 
     @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
@@ -182,59 +186,32 @@
 
         // If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed.
         assertEquals(0,
-                displayPolicy.updateLightNavigationBarLw(
-                        APPEARANCE_LIGHT_NAVIGATION_BARS, null, null,
-                        null, null));
-
-        // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null,
-                opaqueDarkNavBar));
-        assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
-                displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
-                        opaqueLightNavBar, null, opaqueLightNavBar));
-        assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
-                displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS,
-                        opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+                displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null));
 
         // Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
+        assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, dimming));
         assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                0, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar,
-                dimming));
+                APPEARANCE_LIGHT_NAVIGATION_BARS, dimming));
 
-        // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+        // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+        assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
         assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
-                imeDrawDarkNavBar));
-
-        // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
-        assertEquals(0, displayPolicy.updateLightNavigationBarLw(
-                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar,
-                imeDrawDarkNavBar, imeDrawDarkNavBar));
-
-        // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS.
-        assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
-                displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
-                        opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar));
+        assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+                0, opaqueLightNavBar));
+        assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+                APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar));
     }
 
-    @UseTestDisplay(addWindows = W_ACTIVITY)
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_STATUS_BAR})
     @Test
     public void testComputeTopFullscreenOpaqueWindow() {
         final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
         attrs.x = attrs.y = 0;
         attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
         final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+        policy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
+
         policy.applyPostLayoutPolicyLw(
                 mAppWindow, attrs, null /* attached */, null /* imeTarget */);
 
@@ -313,7 +290,9 @@
         displayInfo.logicalHeight = 2000;
         displayInfo.rotation = ROTATION_0;
 
-        displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+        WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
+        displayPolicy.addWindowLw(mNavBarWindow, attrs);
+        mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
         mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
         final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
         mImeWindow.mAboveInsetsState.set(state);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 683ed88..3982a83 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -58,8 +58,6 @@
     static final int DISPLAY_HEIGHT = 1000;
     static final int DISPLAY_DENSITY = 320;
 
-    static final int STATUS_BAR_HEIGHT = 10;
-    static final int NAV_BAR_HEIGHT = 15;
     static final int DISPLAY_CUTOUT_HEIGHT = 8;
     static final int IME_HEIGHT = 415;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f2418c6..a8ede13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -82,7 +82,7 @@
             mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
                     dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
                     null /* options */);
-            return true;
+            return dc.getImeContainer().getConfiguration();
         }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
                 anyInt(), any());
 
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 bf3ed69..5b04c91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -80,7 +81,7 @@
     }
 
     @Test
-    public void testControlsForDispatch_dockedStackVisible() {
+    public void testControlsForDispatch_dockedTaskVisible() {
         addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
@@ -93,7 +94,20 @@
     }
 
     @Test
-    public void testControlsForDispatch_freeformStackVisible() {
+    public void testControlsForDispatch_multiWindowTaskVisible() {
+        addWindow(TYPE_STATUS_BAR, "statusBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
+                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+        // The app must not control any system bars.
+        assertNull(controls);
+    }
+
+    @Test
+    public void testControlsForDispatch_freeformTaskVisible() {
         addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
@@ -101,18 +115,6 @@
                 ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
         final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
 
-        // The app must not control any bars.
-        assertNull(controls);
-    }
-
-    @Test
-    public void testControlsForDispatch_dockedDividerControllerResizing() {
-        addWindow(TYPE_STATUS_BAR, "statusBar");
-        addWindow(TYPE_NAVIGATION_BAR, "navBar");
-        mDisplayContent.getDockedDividerController().setResizing(true);
-
-        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
-
         // The app must not control any system bars.
         assertNull(controls);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 647a898..609d159 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -200,28 +200,37 @@
         assertTrue(mLetterbox.needsApplySurfaceChanges());
 
         mLetterbox.applySurfaceChanges(mTransaction);
-        verify(mTransaction).setAlpha(mSurfaces.top, mDarkScrimAlpha);
+        verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
     }
 
     @Test
-    public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
+    public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
         mLetterbox.applySurfaceChanges(mTransaction);
 
-        assertNull(mSurfaces.behind);
+        assertNull(mSurfaces.fullWindowSurface);
     }
 
     @Test
-    public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
+    public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
         mAreCornersRounded = true;
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
         mLetterbox.applySurfaceChanges(mTransaction);
 
-        assertNotNull(mSurfaces.behind);
+        assertNotNull(mSurfaces.fullWindowSurface);
     }
 
     @Test
-    public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
+    public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
+        mHasWallpaperBackground = true;
+        mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+        mLetterbox.applySurfaceChanges(mTransaction);
+
+        assertNotNull(mSurfaces.fullWindowSurface);
+    }
+
+    @Test
+    public void testNotIntersectsOrFullyContains_cornersRounded() {
         mAreCornersRounded = true;
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
         mLetterbox.applySurfaceChanges(mTransaction);
@@ -249,8 +258,8 @@
         public SurfaceControl right;
         private SurfaceControl.Builder mBottomBuilder;
         public SurfaceControl bottom;
-        private SurfaceControl.Builder mBehindBuilder;
-        public SurfaceControl behind;
+        private SurfaceControl.Builder mFullWindowSurfaceBuilder;
+        public SurfaceControl fullWindowSurface;
 
         @Override
         public SurfaceControl.Builder get() {
@@ -265,8 +274,8 @@
                     mRightBuilder = (SurfaceControl.Builder) i.getMock();
                 } else if (((String) i.getArgument(0)).contains("bottom")) {
                     mBottomBuilder = (SurfaceControl.Builder) i.getMock();
-                } else if (((String) i.getArgument(0)).contains("behind")) {
-                    mBehindBuilder = (SurfaceControl.Builder) i.getMock();
+                } else if (((String) i.getArgument(0)).contains("fullWindow")) {
+                    mFullWindowSurfaceBuilder = (SurfaceControl.Builder) i.getMock();
                 }
                 return i.getMock();
             });
@@ -281,8 +290,8 @@
                     right = control;
                 } else if (i.getMock() == mBottomBuilder) {
                     bottom = control;
-                } else if (i.getMock() == mBehindBuilder) {
-                    behind = control;
+                } else if (i.getMock() == mFullWindowSurfaceBuilder) {
+                    fullWindowSurface = control;
                 }
                 return control;
             }).when(builder).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d017c19..1b19a28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -31,8 +31,8 @@
 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.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 0c6545c..6cc60ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -30,28 +30,28 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 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.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
 import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -89,9 +89,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
 /**
  * Tests for the root {@link Task} behavior.
  *
@@ -280,11 +277,11 @@
     public void testResumedActivity() {
         final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = r.getTask();
-        assertNull(task.getResumedActivity());
+        assertNull(task.getTopResumedActivity());
         r.setState(RESUMED, "testResumedActivity");
-        assertEquals(r, task.getResumedActivity());
+        assertEquals(r, task.getTopResumedActivity());
         r.setState(PAUSING, "testResumedActivity");
-        assertNull(task.getResumedActivity());
+        assertNull(task.getTopResumedActivity());
     }
 
     @Test
@@ -295,15 +292,15 @@
         final Task task = r.getTask();
         // Ensure moving task between two root tasks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
-        assertEquals(r, rootTask.getResumedActivity());
+        assertEquals(r, rootTask.getTopResumedActivity());
 
         final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
         task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
                 false /* animate */, true /* deferResume*/,
                 "testResumedActivityFromTaskReparenting");
 
-        assertNull(rootTask.getResumedActivity());
-        assertEquals(r, destRootTask.getResumedActivity());
+        assertNull(rootTask.getTopResumedActivity());
+        assertEquals(r, destRootTask.getTopResumedActivity());
     }
 
     @Test
@@ -314,15 +311,15 @@
         final Task task = r.getTask();
         // Ensure moving task between two root tasks updates resumed activity
         r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
-        assertEquals(r, rootTask.getResumedActivity());
+        assertEquals(r, rootTask.getTopResumedActivity());
 
         final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
         task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
                 false /* animate */, false /* deferResume*/,
                 "testResumedActivityFromActivityReparenting");
 
-        assertNull(rootTask.getResumedActivity());
-        assertEquals(r, destRootTask.getResumedActivity());
+        assertNull(rootTask.getTopResumedActivity());
+        assertEquals(r, destRootTask.getTopResumedActivity());
     }
 
     @Test
@@ -618,9 +615,9 @@
         doReturn(false).when(splitScreenSecondary2).isTranslucent(any());
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // First split-screen secondary should be visible behind another translucent split-screen
@@ -628,9 +625,9 @@
         doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
@@ -642,13 +639,13 @@
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 assistantRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 splitScreenPrimary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Split-screen root tasks should be visible behind a translucent fullscreen root task.
@@ -657,13 +654,13 @@
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 assistantRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenPrimary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Assistant root task shouldn't be visible behind translucent split-screen root task,
@@ -678,25 +675,25 @@
             assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
             assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-            assertEquals(TASK_VISIBILITY_VISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                     assistantRootTask.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_INVISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                     splitScreenPrimary.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_INVISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                     splitScreenSecondary.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_INVISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                     splitScreenSecondary2.getVisibility(null /* starting */));
         } else {
             assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
             assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
             assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-            assertEquals(TASK_VISIBILITY_INVISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                     assistantRootTask.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_VISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                     splitScreenPrimary.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_INVISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                     splitScreenSecondary.getVisibility(null /* starting */));
-            assertEquals(TASK_VISIBILITY_VISIBLE,
+            assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                     splitScreenSecondary2.getVisibility(null /* starting */));
         }
     }
@@ -707,45 +704,51 @@
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
         final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+        // Creating as two-level tasks so home task can be reparented to split-secondary root task.
         final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */,
+                true /* twoLevelTask */);
 
         doReturn(false).when(homeRootTask).isTranslucent(any());
         doReturn(false).when(splitPrimary).isTranslucent(any());
         doReturn(false).when(splitSecondary).isTranslucent(any());
 
-
         // Re-parent home to split secondary.
         homeRootTask.reparent(splitSecondary, POSITION_TOP);
         // Current tasks should be visible.
-        assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                splitPrimary.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                splitSecondary.getVisibility(null /* starting */));
         // Home task should still be visible even though it is a child of another visible task.
-        assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                homeRootTask.getVisibility(null /* starting */));
 
 
         // Add fullscreen translucent task that partially occludes split tasks
         final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
                 WINDOWING_MODE_FULLSCREEN, true /* translucent */);
         // Fullscreen translucent task should be visible
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
         // Split tasks should be visible behind translucent
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitPrimary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 splitSecondary.getVisibility(null /* starting */));
         // Home task should be visible behind translucent since its parent is visible behind
         // translucent.
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 homeRootTask.getVisibility(null /* starting */));
 
 
         // Hide split-secondary
         splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
         // Home split secondary and home task should be invisible.
-        assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+                splitSecondary.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+                homeRootTask.getVisibility(null /* starting */));
     }
 
     @Test
@@ -757,9 +760,9 @@
                 createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 bottomRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
     }
 
@@ -775,10 +778,12 @@
                 createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+                bottomRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                opaqueRootTask.getVisibility(null /* starting */));
     }
 
     @Test
@@ -793,10 +798,11 @@
                 createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+                bottomRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 opaqueRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
     }
 
@@ -809,9 +815,9 @@
                 createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 bottomTranslucentRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
     }
 
@@ -824,9 +830,10 @@
                 createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
-        assertEquals(TASK_VISIBILITY_INVISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                 bottomTranslucentRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                opaqueRootTask.getVisibility(null /* starting */));
     }
 
     @Test
@@ -840,16 +847,17 @@
         final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                 bottomRootTask.getVisibility(null /* starting */));
-        assertEquals(TASK_VISIBILITY_VISIBLE,
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                 translucentRootTask.getVisibility(null /* starting */));
         // Add an activity to the pinned root task so it isn't considered empty for visibility
         // check.
         final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
                 .setTask(pinnedRootTask)
                 .build();
-        assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
+        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+                pinnedRootTask.getVisibility(null /* starting */));
     }
 
     @Test
@@ -1142,12 +1150,12 @@
         } else if (twoLevelTask) {
             task = new TaskBuilder(mSupervisor)
                     .setTaskDisplayArea(taskDisplayArea)
-                    .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
                     .setOnTop(onTop)
                     .setCreateActivity(true)
                     .setCreateParentTask(true)
                     .build().getRootTask();
+            task.setWindowingMode(windowingMode);
         } else {
             task = new TaskBuilder(mSupervisor)
                     .setTaskDisplayArea(taskDisplayArea)
@@ -1301,9 +1309,9 @@
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
         topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
 
-        task.startPausingLocked(false /* uiSleeping */, topActivity,
+        task.startPausing(false /* uiSleeping */, topActivity,
                 "test");
-        verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
+        verify(task).completePause(anyBoolean(), eq(topActivity));
     }
 
     @Test
@@ -1494,41 +1502,6 @@
     }
 
     @Test
-    public void testIterateOccludedActivity() {
-        final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
-        final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
-        final Task task = new TaskBuilder(mSupervisor).build();
-        final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
-        final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
-        // Top activity occludes bottom activity.
-        doReturn(true).when(task).shouldBeVisible(any());
-        assertTrue(topActivity.shouldBeVisible());
-        assertFalse(bottomActivity.shouldBeVisible());
-
-        task.forAllOccludedActivities(handleOccludedActivity);
-        assertThat(occludedActivities).containsExactly(bottomActivity);
-
-        // Top activity doesn't occlude parent, so the bottom activity is not occluded.
-        doReturn(false).when(topActivity).occludesParent();
-        assertTrue(bottomActivity.shouldBeVisible());
-
-        occludedActivities.clear();
-        task.forAllOccludedActivities(handleOccludedActivity);
-        assertThat(occludedActivities).isEmpty();
-
-        // A finishing activity should not occlude other activities behind.
-        final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
-        finishingActivity.finishing = true;
-        doCallRealMethod().when(finishingActivity).occludesParent();
-        assertTrue(topActivity.shouldBeVisible());
-        assertTrue(bottomActivity.shouldBeVisible());
-
-        occludedActivities.clear();
-        task.forAllOccludedActivities(handleOccludedActivity);
-        assertThat(occludedActivities).isEmpty();
-    }
-
-    @Test
     public void testClearUnknownAppVisibilityBehindFullscreenActivity() {
         final UnknownAppVisibilityController unknownAppVisibilityController =
                 mDefaultTaskDisplayArea.mDisplayContent.mUnknownAppVisibilityController;
@@ -1544,7 +1517,7 @@
             activities[i] = r;
             doReturn(null).when(mAtm).getProcessController(
                     eq(r.processName), eq(r.info.applicationInfo.uid));
-            r.setState(Task.ActivityState.INITIALIZING, "test");
+            r.setState(INITIALIZING, "test");
             // Ensure precondition that the activity is opaque.
             assertTrue(r.occludesParent());
             mSupervisor.startSpecificActivity(r, false /* andResume */,
@@ -1552,14 +1525,13 @@
         }
         mSupervisor.endDeferResume();
 
-        setBooted(mAtm);
         // 2 activities are started while keyguard is locked, so they are waiting to be resolved.
         assertFalse(unknownAppVisibilityController.allResolved());
 
-        // Assume the top activity is going to resume and
-        // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown
-        // visibility records that are occluded.
-        task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+        // Any common path that updates activity visibility should clear the unknown visibility
+        // records that are no longer visible according to hierarchy.
+        task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */);
         // Assume the top activity relayouted, just remove it directly.
         unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
         // All unresolved records should be removed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9cf29d4..8d4acbb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -31,13 +31,14 @@
 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.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-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.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -365,7 +366,7 @@
         TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
         doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
         mRootWindowContainer.applySleepTokens(true);
-        verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
         verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
                 null /* target */, null /* targetOptions */);
     }
@@ -386,7 +387,7 @@
         // landscape and the portrait lockscreen is shown.
         activity.setLastReportedConfiguration(
                 new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
-        activity.setState(Task.ActivityState.STOPPED, "sleep");
+        activity.setState(STOPPED, "sleep");
 
         display.setIsSleeping(true);
         doReturn(false).when(display).shouldSleep();
@@ -557,8 +558,8 @@
         doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         // Use the task as target to resume.
-        mRootWindowContainer.resumeFocusedTasksTopActivities(
-                rootTask, activity, null /* targetOptions */);
+        mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity,
+                null /* targetOptions */);
 
         // Verify the target task should resume its activity.
         verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
@@ -626,7 +627,7 @@
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setTask(rootTask).setOnTop(true).build();
-        activity.setState(Task.ActivityState.RESUMED, "test");
+        activity.setState(RESUMED, "test");
 
         // Assume the task is at the topmost position
         assertTrue(rootTask.isTopRootTaskInDisplayArea());
@@ -646,7 +647,7 @@
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setTask(rootTask).setOnTop(true).build();
-        activity.setState(Task.ActivityState.RESUMED, "test");
+        activity.setState(RESUMED, "test");
         taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
 
         // Assume the task is at the topmost position
@@ -774,7 +775,7 @@
     }
 
     /**
-     * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+     * Tests that when starting {@link ResolverActivity} for home, it should use the standard
      * activity type (in a new root task) so the order of back stack won't be broken.
      */
     @Test
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 f35e85c..5989497 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,8 +40,10 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 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.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -130,7 +132,7 @@
         doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
         mActivity.mVisibleRequested = true;
         mActivity.setSavedState(null /* savedState */);
-        mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
+        mActivity.setState(RESUMED, "testRestart");
         prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
 
         final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
@@ -138,7 +140,7 @@
         // The visible activity should recompute configuration according to the last parent bounds.
         mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
 
-        assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
+        assertEquals(RESTARTING_PROCESS, mActivity.getState());
         assertNotEquals(originalOverrideBounds, mActivity.getBounds());
     }
 
@@ -585,7 +587,7 @@
     public void testHandleActivitySizeCompatModeChanged() {
         setUpDisplaySizeWithApp(1000, 2000);
         doReturn(true).when(mTask).isOrganized();
-        mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+        mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
@@ -604,7 +606,7 @@
         mActivity.mVisibleRequested = true;
         mActivity.restartProcessIfVisible();
         // The full lifecycle isn't hooked up so manually set state to resumed
-        mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+        mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
         mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
 
         // Expect null token when switching to non-size-compat mode activity.
@@ -618,7 +620,7 @@
     public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
         setUpDisplaySizeWithApp(1000, 2000);
         doReturn(true).when(mTask).isOrganized();
-        mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+        mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
@@ -637,7 +639,7 @@
                 .setCreateActivity(true).build();
         final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
         doReturn(true).when(secondTask).isOrganized();
-        secondActivity.setState(Task.ActivityState.RESUMED,
+        secondActivity.setState(RESUMED,
                 "testHandleActivitySizeCompatModeChanged");
         prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
 
@@ -1553,6 +1555,30 @@
     }
 
     @Test
+    public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
+        // Set up a display in landscape with an unresizable app.
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
+        prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+        assertFitted();
+
+        // Activity max bounds not be sandboxed since sandboxing is disabled.
+        assertMaxBoundsInheritDisplayAreaBounds();
+    }
+
+    @Test
+    public void testSandboxDisplayApis_unresizableAppSandboxed() {
+        // Set up a display in landscape with an unresizable app.
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
+        prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+        assertFitted();
+
+        // Activity max bounds should be sandboxed since sandboxing is enabled.
+        assertActivityMaxBoundsSandboxed();
+    }
+
+    @Test
     public void testResizableApp_notSandboxed() {
         // Set up a display in landscape with a fully resizable app.
         setUpDisplaySizeWithApp(2500, 1000);
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 b89539c..e32b2aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -178,6 +178,11 @@
     }
 
     @Override
+    public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) {
+        return this;
+    }
+
+    @Override
     public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken,
             int orientation, Rect layerStackRect, Rect displayRect) {
         return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 67b273a..c45c18d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -37,8 +37,8 @@
 
 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.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -84,8 +84,7 @@
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         adjacentRootTask.mCreatedByOrganizer = true;
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
-        adjacentRootTask.mAdjacentTask = rootTask;
-        rootTask.mAdjacentTask = adjacentRootTask;
+        adjacentRootTask.setAdjacentTaskFragment(rootTask);
 
         taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
         Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -111,8 +110,7 @@
         final Task adjacentRootTask = createTask(
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         adjacentRootTask.mCreatedByOrganizer = true;
-        adjacentRootTask.mAdjacentTask = rootTask;
-        rootTask.mAdjacentTask = adjacentRootTask;
+        adjacentRootTask.setAdjacentTaskFragment(rootTask);
 
         taskDisplayArea.setLaunchRootTask(rootTask,
                 new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -133,8 +131,7 @@
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         adjacentRootTask.mCreatedByOrganizer = true;
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
-        adjacentRootTask.mAdjacentTask = rootTask;
-        rootTask.mAdjacentTask = adjacentRootTask;
+        adjacentRootTask.setAdjacentTaskFragment(rootTask);
 
         taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
         final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
new file mode 100644
index 0000000..3dd3e81
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentCreationParams;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizer;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:TaskFragmentOrganizerControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
+
+    private TaskFragmentOrganizerController mController;
+    private TaskFragmentOrganizer mOrganizer;
+    private ITaskFragmentOrganizer mIOrganizer;
+    private TaskFragment mTaskFragment;
+    private TaskFragmentInfo mTaskFragmentInfo;
+    private IBinder mFragmentToken;
+    private WindowContainerTransaction mTransaction;
+    private WindowContainerToken mFragmentWindowToken;
+
+    @Before
+    public void setup() {
+        mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+        mOrganizer = new TaskFragmentOrganizer(Runnable::run);
+        mIOrganizer = mOrganizer.getIOrganizer();
+        mTaskFragmentInfo = mock(TaskFragmentInfo.class);
+        mFragmentToken = new Binder();
+        mTaskFragment =
+                new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
+        mTransaction = new WindowContainerTransaction();
+        mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+
+        spyOn(mController);
+        spyOn(mOrganizer);
+        spyOn(mTaskFragment);
+        doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
+        doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
+        doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
+        doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
+        doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+    }
+
+    @Test
+    public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+        assertThrows(IllegalArgumentException.class, () -> mController
+                .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+        assertThrows(IllegalArgumentException.class, () -> mController
+                .onTaskFragmentInfoChanged(
+                        mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+        assertThrows(IllegalArgumentException.class, () -> mController
+                .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+        assertThrows(IllegalArgumentException.class, () -> mController
+                .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+                        mTaskFragment));
+    }
+
+    @Test
+    public void testOnTaskFragmentAppeared() {
+        mController.registerOrganizer(mIOrganizer);
+
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentAppeared(any());
+    }
+
+    @Test
+    public void testOnTaskFragmentInfoChanged() {
+        mController.registerOrganizer(mIOrganizer);
+        mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        // No callback if the info is not changed.
+        doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+        doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+
+        mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+                mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+        // Trigger callback if the info is changed.
+        doReturn(false).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+
+        mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+                mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentInfoChanged(mTaskFragmentInfo);
+    }
+
+    @Test
+    public void testOnTaskFragmentVanished() {
+        mController.registerOrganizer(mIOrganizer);
+
+        mTaskFragment.mTaskFragmentAppearedSent = true;
+        mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentVanished(any());
+    }
+
+    @Test
+    public void testOnTaskFragmentParentInfoChanged() {
+        mController.registerOrganizer(mIOrganizer);
+        final Task parent = mock(Task.class);
+        final Configuration parentConfig = new Configuration();
+        parentConfig.smallestScreenWidthDp = 10;
+        doReturn(parent).when(mTaskFragment).getParent();
+        doReturn(parentConfig).when(parent).getConfiguration();
+        doReturn(parent).when(parent).asTask();
+
+        mTaskFragment.mTaskFragmentAppearedSent = true;
+        mController.onTaskFragmentParentInfoChanged(
+                mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+
+        // No extra callback if the info is not changed.
+        clearInvocations(mOrganizer);
+
+        mController.onTaskFragmentParentInfoChanged(
+                mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+
+        // Trigger callback if the info is changed.
+        parentConfig.smallestScreenWidthDp = 100;
+
+        mController.onTaskFragmentParentInfoChanged(
+                mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+    }
+
+    @Test
+    public void testOnTaskFragmentError() throws RemoteException {
+        final IBinder errorCallbackToken = new Binder();
+        final Throwable exception = new IllegalArgumentException("Test exception");
+
+        mController.registerOrganizer(mIOrganizer);
+        mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(),
+                errorCallbackToken, exception);
+        mController.dispatchPendingEvents();
+
+        verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
+    }
+
+    @Test
+    public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
+        mOrganizer.applyTransaction(mTransaction);
+
+        assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
+
+        mTransaction = new WindowContainerTransaction();
+        mOrganizer.applySyncTransaction(
+                mTransaction, mock(WindowContainerTransactionCallback.class));
+
+        assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
+    }
+
+    @Test
+    public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment()
+            throws RemoteException {
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Throw exception if the transaction is trying to change a window that is not organized by
+        // the organizer.
+        mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+
+        assertThrows(SecurityException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+
+        // Allow transaction to change a TaskFragment created by the organizer.
+        mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+    }
+
+    @Test
+    public void testApplyTransaction_enforceHierarchyChange_reorder() throws RemoteException {
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Throw exception if the transaction is trying to change a window that is not organized by
+        // the organizer.
+        mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
+
+        assertThrows(SecurityException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+
+        // Allow transaction to change a TaskFragment created by the organizer.
+        mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+    }
+
+    @Test
+    public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()
+            throws RemoteException {
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Throw exception if the transaction is trying to change a window that is not organized by
+        // the organizer.
+        mTransaction.deleteTaskFragment(mFragmentWindowToken);
+
+        assertThrows(SecurityException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+
+        // Allow transaction to change a TaskFragment created by the organizer.
+        mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+    }
+
+    @Test
+    public void testApplyTransaction_enforceHierarchyChange_setAdjacentRoots()
+            throws RemoteException {
+        final TaskFragment taskFragment2 =
+                new TaskFragment(mAtm, new Binder(), true /* createdByOrganizer */);
+        final WindowContainerToken token2 = taskFragment2.mRemoteToken.toWindowContainerToken();
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Throw exception if the transaction is trying to change a window that is not organized by
+        // the organizer.
+        mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
+
+        assertThrows(SecurityException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+
+        // Allow transaction to change a TaskFragment created by the organizer.
+        mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+        taskFragment2.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+    }
+
+    @Test
+    public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
+        mTransaction.createTaskFragment(mock(TaskFragmentCreationParams.class));
+        mTransaction.startActivityInTaskFragment(
+                mFragmentToken, new Intent(), null /* activityOptions */);
+        mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
+
+        // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
+        // testing the security check here.
+        assertThrows(IllegalArgumentException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+    }
+
+    @Test
+    public void testApplyTransaction_enforceHierarchyChange_reparentChildren()
+            throws RemoteException {
+        mOrganizer.applyTransaction(mTransaction);
+
+        // Throw exception if the transaction is trying to change a window that is not organized by
+        // the organizer.
+        mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
+
+        assertThrows(SecurityException.class, () -> {
+            try {
+                mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+            } catch (RemoteException e) {
+                fail();
+            }
+        });
+
+        // Allow transaction to change a TaskFragment created by the organizer.
+        mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0ebff1d..629e452 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1257,7 +1257,8 @@
         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
         spyOn(persister);
 
-        final Task task = getTestTask();
+        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setCreateParentTask(true).build().getRootTask();
         task.setHasBeenVisible(false);
         task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
         task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2dfb3a1..45e5f8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -339,6 +339,44 @@
     }
 
     @Test
+    public void testTargets_noIntermediatesToWallpaper() {
+        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+        // Make DA organized so we can check that they don't get included.
+        WindowContainer parent = wallpaperWindowToken.getParent();
+        while (parent != null && parent != mDisplayContent) {
+            if (parent.asDisplayArea() != null) {
+                parent.asDisplayArea().setOrganizer(
+                        mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
+            }
+            parent = parent.getParent();
+        }
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+        wallpaperWindowToken.setVisibleRequested(false);
+        transition.collect(wallpaperWindowToken);
+        wallpaperWindowToken.setVisibleRequested(true);
+        wallpaperWindow.mHasSurface = true;
+        doReturn(true).when(mDisplayContent).isAttached();
+        transition.collect(mDisplayContent);
+        mDisplayContent.getWindowConfiguration().setRotation(
+                (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
+
+        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+                transition.mParticipants, transition.mChanges);
+        TransitionInfo info = Transition.calculateTransitionInfo(
+                0, 0, targets, transition.mChanges);
+        // The wallpaper is not organized, so it won't have a token; however, it will be marked
+        // as IS_WALLPAPER
+        assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
+        // Make sure no intermediate display areas were pulled in between wallpaper and display.
+        assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
+                info.getChanges().get(0).getParent());
+    }
+
+    @Test
     public void testIndependent() {
         final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index a1f89ec..efe6538 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,12 +57,14 @@
 public class WindowFrameTests extends WindowTestsBase {
 
     private DisplayContent mTestDisplayContent;
+    private DisplayFrames mTestDisplayFrames;
 
     @Before
     public void setUp() throws Exception {
         DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
         testDisplayInfo.displayCutout = null;
         mTestDisplayContent = createNewDisplay(testDisplayInfo);
+        mTestDisplayFrames = mTestDisplayContent.mDisplayFrames;
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
@@ -99,7 +101,7 @@
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
         w.getWindowFrames().setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 0, 0, 1000, 1000);
         assertRelFrame(w, 0, 0, 1000, 1000);
 
@@ -108,14 +110,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertFrame(w, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertFrame(w, 0, 0, 300, 300);
@@ -128,14 +130,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -143,7 +145,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -153,18 +155,18 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 700, 0, 1000, 300);
         assertRelFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 700, 700, 1000, 1000);
         assertRelFrame(w, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, 600, 600, 900, 900);
         assertRelFrame(w, 600, 600, 900, 900);
     }
@@ -191,7 +193,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertEquals(resolvedTaskBounds, w.getFrame());
@@ -204,7 +206,7 @@
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
         windowFrames.setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertEquals(resolvedTaskBounds, w.getFrame());
         assertEquals(0, w.getRelativeFrame().left);
         assertEquals(0, w.getRelativeFrame().top);
@@ -233,7 +235,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -249,7 +251,7 @@
         task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         task.setBounds(null);
         windowFrames.setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, cf);
     }
 
@@ -285,7 +287,7 @@
         final Rect winRect = new Rect(200, 200, 300, 500);
         task.setBounds(winRect);
         w.getWindowFrames().setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top);
 
         // Now check that it won't get moved beyond the top
@@ -293,7 +295,7 @@
         task.setBounds(winRect);
         w.setBounds(winRect);
         w.getWindowFrames().setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
 
         // Now we have status bar. Check that it won't go into the status bar area.
@@ -301,14 +303,14 @@
         statusBarFrame.bottom = 60;
         state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame);
         w.getWindowFrames().setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right,
                 statusBarFrame.bottom + winRect.height());
 
         // Check that it's moved back without ime insets
         state.removeSource(ITYPE_IME);
         w.getWindowFrames().setFrames(pf, pf);
-        w.computeFrame();
+        w.computeFrame(mTestDisplayFrames);
         assertEquals(winRect, w.getFrame());
     }
 
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 d9aa871..83cdc3ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -107,9 +107,9 @@
         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
-        mWm.handleTaskFocusChange(tappedTask);
+        mWm.handleTaskFocusChange(tappedTask, null /* window */);
 
-        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
     }
 
     @Test
@@ -128,9 +128,9 @@
         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
-        mWm.handleTaskFocusChange(tappedTask);
+        mWm.handleTaskFocusChange(tappedTask, null /* window */);
 
-        verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId, null);
     }
 
     @Test
@@ -151,9 +151,9 @@
         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
         spyOn(mWm.mAtmService);
 
-        mWm.handleTaskFocusChange(tappedTask);
+        mWm.handleTaskFocusChange(tappedTask, null /* window */);
 
-        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
     }
 
     @Test
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 d6a8401..39fe952 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,8 +42,7 @@
 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.Task.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
 
@@ -62,6 +61,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
@@ -70,7 +70,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -346,7 +345,7 @@
     @Test
     public void testDisplayAreaTransaction() {
         removeGlobalMinSizeRestriction();
-        final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+        final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
         testTransaction(displayArea);
     }
 
@@ -364,7 +363,7 @@
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         testSetWindowingMode(rootTask);
 
-        final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+        final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
         displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
         testSetWindowingMode(displayArea);
     }
@@ -1256,21 +1255,24 @@
     @Test
     public void testStartTasksInTransaction() {
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        Bundle testOptions = new Bundle();
-        testOptions.putInt("test", 20);
+        ActivityOptions testOptions = ActivityOptions.makeBasic();
+        testOptions.setTransientLaunch();
         wct.startTask(1, null /* options */);
-        wct.startTask(2, testOptions);
-        spyOn(mWm.mAtmService);
-        doReturn(START_CANCELED).when(mWm.mAtmService).startActivityFromRecents(anyInt(), any());
+        wct.startTask(2, testOptions.toBundle());
+        spyOn(mWm.mAtmService.mTaskSupervisor);
+        doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
+                anyInt(), anyInt(), anyInt(), any());
         clearInvocations(mWm.mAtmService);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
 
-        final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
-        verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(1), bundleCaptor.capture());
-        assertTrue(bundleCaptor.getValue().isEmpty());
+        verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+                anyInt(), anyInt(), eq(1), any());
 
-        verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(2), bundleCaptor.capture());
-        assertEquals(20, bundleCaptor.getValue().getInt("test"));
+        final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
+                ArgumentCaptor.forClass(SafeActivityOptions.class);
+        verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+                anyInt(), anyInt(), eq(2), optionsCaptor.capture());
+        assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
     }
 
     /**
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 ed18d26..d3f2d14 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -23,6 +23,12 @@
 
 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.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -311,17 +317,17 @@
 
         callbackResult[0] = 0;
         activity.mVisibleRequested = false;
-        activity.setState(Task.ActivityState.PAUSED, "test");
+        activity.setState(PAUSED, "test");
         mWpc.computeOomAdjFromActivities(callback);
         assertEquals(paused, callbackResult[0]);
 
         callbackResult[0] = 0;
-        activity.setState(Task.ActivityState.STOPPING, "test");
+        activity.setState(STOPPING, "test");
         mWpc.computeOomAdjFromActivities(callback);
         assertEquals(stopping, callbackResult[0]);
 
         callbackResult[0] = 0;
-        activity.setState(Task.ActivityState.STOPPED, "test");
+        activity.setState(STOPPED, "test");
         mWpc.computeOomAdjFromActivities(callback);
         assertEquals(other, callbackResult[0]);
     }
@@ -332,25 +338,25 @@
         spyOn(tracker);
         final ActivityRecord activity = createActivityRecord(mWpc);
         activity.mVisibleRequested = true;
-        activity.setState(Task.ActivityState.STARTED, "test");
+        activity.setState(STARTED, "test");
 
         verify(tracker).onAnyActivityVisible(mWpc);
         assertTrue(mWpc.hasVisibleActivities());
 
-        activity.setState(Task.ActivityState.RESUMED, "test");
+        activity.setState(RESUMED, "test");
 
         verify(tracker).onActivityResumedWhileVisible(mWpc);
         assertTrue(tracker.hasResumedActivity(mWpc.mUid));
 
         activity.makeFinishingLocked();
-        activity.setState(Task.ActivityState.PAUSING, "test");
+        activity.setState(PAUSING, "test");
 
         assertFalse(tracker.hasResumedActivity(mWpc.mUid));
         assertTrue(mWpc.hasForegroundActivities());
 
         activity.setVisibility(false);
         activity.mVisibleRequested = false;
-        activity.setState(Task.ActivityState.STOPPED, "test");
+        activity.setState(STOPPED, "test");
 
         verify(tracker).onAllActivitiesInvisible(mWpc);
         assertFalse(mWpc.hasVisibleActivities());
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 92b670e..3929082 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -53,6 +53,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -891,6 +894,36 @@
         assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
     }
 
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testAdjustImeInsetsVisibilityWhenTaskSwitchIsAnimating() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+        controller.getImeSourceProvider().setWindow(mImeWindow, null, null);
+
+        // Simulate app requests IME with updating all windows Insets State when IME is above app.
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+        assertTrue(mDisplayContent.shouldImeAttachedToApp());
+        controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+        controller.getImeSourceProvider().getSource().setVisible(true);
+        controller.updateAboveInsetsState(mImeWindow, false);
+
+        // Simulate task switching animation happens when switching app to app2.
+        spyOn(app);
+        spyOn(app2);
+        doReturn(true).when(app).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
+        doReturn(true).when(app2).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
+        app.mActivityRecord.mLastImeShown = true;
+
+        // Verify the IME insets is visible on app, but not for app2 during task animating.
+        InsetsState stateApp = app.getInsetsState();
+        InsetsState stateApp2 = app2.getInsetsState();
+        assertTrue(stateApp.getSource(ITYPE_IME).isVisible());
+        assertFalse(stateApp2.getSource(ITYPE_IME).isVisible());
+    }
+
     @UseTestDisplay(addWindows = { W_ACTIVITY })
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
@@ -946,4 +979,19 @@
         assertNotNull(state.peekSource(ITYPE_IME));
         assertTrue(state.getSource(ITYPE_IME).isVisible());
     }
+
+    @Test
+    public void testRequestedVisibility() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        app.mActivityRecord.setVisible(false);
+        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        assertFalse(app.isVisibleRequested());
+
+        // It doesn't have a surface yet, but should still be visible requested.
+        app.setHasSurface(false);
+        app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+
+        assertFalse(app.isVisible());
+        assertTrue(app.isVisibleRequested());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5880899..050fd80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -29,11 +30,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.os.Process.SYSTEM_UID;
 import static android.view.View.VISIBLE;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -83,10 +86,12 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
 import android.view.IWindow;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
@@ -132,6 +137,9 @@
     DisplayInfo mDisplayInfo = new DisplayInfo();
     DisplayContent mDefaultDisplay;
 
+    static final int STATUS_BAR_HEIGHT = 10;
+    static final int NAV_BAR_HEIGHT = 15;
+
     /**
      * It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with
      * {@link UseTestDisplay}, it will be an additional display.
@@ -268,6 +276,14 @@
         }
         if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) {
             mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
+            if (INSETS_LAYOUT_GENERALIZATION) {
+                mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT;
+                mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+                mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
+                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+                mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT,
+                        STATUS_BAR_HEIGHT);
+            }
         }
         if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
             mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
@@ -275,6 +291,15 @@
         }
         if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) {
             mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
+            if (INSETS_LAYOUT_GENERALIZATION) {
+                mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT;
+                mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+                mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
+                for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+                    mNavBarWindow.mAttrs.paramsForRotation[rot] =
+                            getNavBarLayoutParamsForRotation(rot);
+                }
+            }
         }
         if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) {
             mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER,
@@ -302,6 +327,37 @@
         waitUntilHandlersIdle();
     }
 
+    private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) {
+        int width = WindowManager.LayoutParams.MATCH_PARENT;
+        int height = WindowManager.LayoutParams.MATCH_PARENT;
+        int gravity = Gravity.BOTTOM;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            switch (rotation) {
+                case ROTATION_UNDEFINED:
+                case Surface.ROTATION_0:
+                case Surface.ROTATION_180:
+                    height = NAV_BAR_HEIGHT;
+                    break;
+                case Surface.ROTATION_90:
+                    gravity = Gravity.RIGHT;
+                    width = NAV_BAR_HEIGHT;
+                    break;
+                case Surface.ROTATION_270:
+                    gravity = Gravity.LEFT;
+                    width = NAV_BAR_HEIGHT;
+                    break;
+            }
+        }
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR);
+        lp.width = width;
+        lp.height = height;
+        if (INSETS_LAYOUT_GENERALIZATION) {
+            lp.gravity = gravity;
+        }
+        return lp;
+    }
+
     void beforeCreateTestDisplay() {
         // Called before display is created.
     }
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 9daa093..6c20e74 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -2,3 +2,5 @@
 varunshah@google.com
 huiyu@google.com
 yamasani@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index b056de0..763159a 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -208,7 +208,7 @@
     public boolean isReservedSupported(String volumeUuid, String callingPackage) {
         if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
             return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false)
-                    || Build.IS_CONTAINER;
+                    || Build.IS_ARC;
         } else {
             return false;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f24c36..b966643 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -239,8 +239,7 @@
                 mHandler.updateState(state);
             } else if ("GETPROTOCOL".equals(accessory)) {
                 if (DEBUG) Slog.d(TAG, "got accessory get protocol");
-                long elapsedRealtime = SystemClock.elapsedRealtime();
-                mHandler.setAccessoryUEventTime(elapsedRealtime);
+                mHandler.setAccessoryUEventTime(SystemClock.elapsedRealtime());
                 resetAccessoryHandshakeTimeoutHandler();
             } else if ("SENDSTRING".equals(accessory)) {
                 if (DEBUG) Slog.d(TAG, "got accessory send string");
@@ -465,6 +464,8 @@
         }
     }
 
+    //TODO It is not clear that this method serves any purpose (at least on Pixel devices)
+    // consider removing
     private static void initRndisAddress() {
         // configure RNDIS ethernet address based on our serial number using the same algorithm
         // we had been previously using in kernel board files
@@ -484,7 +485,7 @@
         try {
             FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
         } catch (IOException e) {
-            Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+            Slog.i(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
         }
     }
 
@@ -760,9 +761,6 @@
         }
 
         private void broadcastUsbAccessoryHandshake() {
-            long elapsedRealtime = SystemClock.elapsedRealtime();
-            long accessoryHandShakeEnd = elapsedRealtime;
-
             // send a sticky broadcast containing USB accessory handshake information
             Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
                     .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
@@ -772,7 +770,7 @@
                     .putExtra(UsbManager.EXTRA_ACCESSORY_START,
                             mStartAccessory)
                     .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
-                            accessoryHandShakeEnd);
+                            SystemClock.elapsedRealtime());
 
             if (DEBUG) {
                 Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
@@ -780,7 +778,6 @@
 
             sendStickyBroadcast(intent);
             resetUsbAccessoryHandshakeDebuggingInfo();
-            accessoryHandShakeEnd = 0L;
         }
 
         protected void updateUsbStateBroadcastIfNeeded(long functions) {
@@ -1935,14 +1932,14 @@
                     }
                     break;
                 case MSG_GET_CURRENT_USB_FUNCTIONS:
-                    Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+                    Slog.i(TAG, "processing MSG_GET_CURRENT_USB_FUNCTIONS");
                     mCurrentUsbFunctionsReceived = true;
 
                     if (mCurrentUsbFunctionsRequested) {
-                        Slog.e(TAG, "updating mCurrentFunctions");
+                        Slog.i(TAG, "updating mCurrentFunctions");
                         // Mask out adb, since it is stored in mAdbEnabled
                         mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
-                        Slog.e(TAG,
+                        Slog.i(TAG,
                                 "mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
                         mCurrentFunctionsApplied = msg.arg1 == 1;
                     }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index a3b5fc7..be37a91 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -36,7 +36,6 @@
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
 import android.hardware.soundtrigger.SoundTriggerModule;
 import android.os.Binder;
 import android.os.DeadObjectException;
@@ -109,9 +108,6 @@
     private boolean mCallActive = false;
     private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode =
             PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
-    // Indicates if the native sound trigger service is disabled or not.
-    // This is an indirect indication of the microphone being open in some other application.
-    private boolean mServiceDisabled = false;
 
     // Whether ANY recognition (keyphrase or generic) has been requested.
     private boolean mRecognitionRequested = false;
@@ -862,23 +858,19 @@
     }
 
     @Override
-    public void onSoundModelUpdate(SoundModelEvent event) {
-        if (event == null) {
-            Slog.w(TAG, "Invalid sound model event!");
-            return;
-        }
-        if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
+    public void onModelUnloaded(int modelHandle) {
+        if (DBG) Slog.d(TAG, "onModelUnloaded: " + modelHandle);
         synchronized (mLock) {
             MetricsLogger.count(mContext, "sth_sound_model_updated", 1);
-            onSoundModelUpdatedLocked(event);
+            onModelUnloadedLocked(modelHandle);
         }
     }
 
     @Override
-    public void onServiceStateChange(int state) {
-        if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
+    public void onResourcesAvailable() {
+        if (DBG) Slog.d(TAG, "onResourcesAvailable");
         synchronized (mLock) {
-            onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
+            onResourcesAvailableLocked();
         }
     }
 
@@ -910,15 +902,14 @@
         updateAllRecognitionsLocked();
     }
 
-    private void onSoundModelUpdatedLocked(SoundModelEvent event) {
-        // TODO: Handle sound model update here.
+    private void onModelUnloadedLocked(int modelHandle) {
+        ModelData modelData = getModelDataForLocked(modelHandle);
+        if (modelData != null) {
+            modelData.setNotLoaded();
+        }
     }
 
-    private void onServiceStateChangedLocked(boolean disabled) {
-        if (disabled == mServiceDisabled) {
-            return;
-        }
-        mServiceDisabled = disabled;
+    private void onResourcesAvailableLocked() {
         updateAllRecognitionsLocked();
     }
 
@@ -1039,7 +1030,6 @@
             if (mModule != null) {
                 mModule.detach();
                 mModule = null;
-                mServiceDisabled = false;
             }
         }
     }
@@ -1114,8 +1104,6 @@
             pw.print("  call active=");
             pw.println(mCallActive);
             pw.println("  SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
-            pw.print("  service disabled=");
-            pw.println(mServiceDisabled);
         }
     }
 
@@ -1329,8 +1317,7 @@
             mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode();
         }
 
-        return !mCallActive && !mServiceDisabled
-                && isRecognitionAllowedByPowerState(
+        return !mCallActive && isRecognitionAllowedByPowerState(
                 modelData);
     }
 
@@ -1571,6 +1558,10 @@
             mModelState = MODEL_LOADED;
         }
 
+        synchronized void setNotLoaded() {
+            mModelState = MODEL_NOTLOADED;
+        }
+
         synchronized boolean isModelStarted() {
             return mModelState == MODEL_STARTED;
         }
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 1963ff3..391dce1 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -23,6 +23,46 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
+    },
+    {
+      "name": "CtsTelephonySdk28TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephony2TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephony3TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsSimRestrictedApisTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephonyProviderTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ],
   "presubmit-large": [
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 759afd7..30fd528 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -775,6 +775,21 @@
             "android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE";
 
     /**
+     * The Telecom call ID of the conference an existing connection should be added to.  This is
+     * required when {@link com.android.services.telephony.TelephonyConnectionService} adds a
+     * {@link Conference} to Telecom using the
+     * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection, Conference)}
+     * API.  That API specifies a parent conference associated with the new existing connection
+     * being added, and there is no equivalent as part of the {@link RemoteConnectionService} API.
+     * This extra key is used to stack the ID of the conference to which the existing connection
+     * will be added so that Telecom can link it up correctly when the {@link RemoteConference}
+     * is added to Telecom by the connection manager.
+     * @hide
+     */
+    public static final String EXTRA_ADD_TO_CONFERENCE_ID =
+            "android.telecom.extra.ADD_TO_CONFERENCE_ID";
+
+    /**
      * Extra key set from a {@link ConnectionService} when using the remote connection APIs
      * (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle,
      * ConnectionRequest, boolean)}) to create a remote connection.  Provides the receiving
@@ -1765,11 +1780,13 @@
         public abstract void onSetDeviceOrientation(int rotation);
 
         /**
-         * Sets camera zoom ratio.
+         * Sets the camera zoom ratio.
          * <p>
          * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
          *
-         * @param value The camera zoom ratio.
+         * @param value The camera zoom ratio; for the current camera, should be a value in the
+         * range defined by
+         * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
          */
         public abstract void onSetZoom(float value);
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index f20ee7e..c365648 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -764,11 +764,13 @@
         public abstract void setDeviceOrientation(int rotation);
 
         /**
-         * Sets camera zoom ratio.
+         * Sets the camera zoom ratio.
          * <p>
          * Handled by {@link Connection.VideoProvider#onSetZoom(float)}.
          *
-         * @param value The camera zoom ratio.
+         * @param value The camera zoom ratio; for the current camera, should be a value in the
+         * range defined by
+         * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
          */
         public abstract void setZoom(float value);
 
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index a427ed61..02bd001 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -139,6 +139,8 @@
      */
     private final int mTargetSdkVersion;
 
+    private final Object mLock = new Object();
+
     Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
         mInCallAdapter = adapter;
         mCallingPackage = callingPackage;
@@ -156,8 +158,12 @@
         if (call == null) {
             call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                     parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
-            mCallByTelecomCallId.put(parcelableCall.getId(), call);
-            mCalls.add(call);
+
+            synchronized (mLock) {
+                mCallByTelecomCallId.put(parcelableCall.getId(), call);
+                mCalls.add(call);
+            }
+
             checkCallTree(parcelableCall);
             call.internalUpdate(parcelableCall, mCallByTelecomCallId);
             fireCallAdded(call);
@@ -169,8 +175,10 @@
     }
 
     final void internalRemoveCall(Call call) {
-        mCallByTelecomCallId.remove(call.internalGetCallId());
-        mCalls.remove(call);
+        synchronized (mLock) {
+            mCallByTelecomCallId.remove(call.internalGetCallId());
+            mCalls.remove(call);
+        }
 
         InCallService.VideoCall videoCall = call.getVideoCall();
         if (videoCall != null) {
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index e1bcb5f..e3485de 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -34,7 +34,10 @@
  * <ul>
  *  <li>The component name of the associated connection service.</li>
  *  <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
- *      component name.</li>
+ *      component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ *      {@link #getId()} provided does not expose personally identifying information.  A
+ *      {@link ConnectionService} should use an opaque token as the {@link PhoneAccountHandle}
+ *      identifier.</li>
  * </ul>
  *
  * Note: This Class requires a non-null {@link ComponentName} and {@link UserHandle} to operate
@@ -49,12 +52,35 @@
     private final String mId;
     private final UserHandle mUserHandle;
 
+    /**
+     * Creates a new {@link PhoneAccountHandle}.
+     *
+     * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+     *                      services this {@link PhoneAccountHandle}.
+     * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+     *           component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+     *           ID provided does not expose personally identifying information.  A
+     *           {@link ConnectionService} should use an opaque token as the
+     *           {@link PhoneAccountHandle} identifier.
+     */
     public PhoneAccountHandle(
             @NonNull ComponentName componentName,
             @NonNull String id) {
         this(componentName, id, Process.myUserHandle());
     }
 
+    /**
+     * Creates a new {@link PhoneAccountHandle}.
+     *
+     * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+     *                      services this {@link PhoneAccountHandle}.
+     * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+     *           component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+     *           ID provided does not expose personally identifying information.  A
+     *           {@link ConnectionService} should use an opaque token as the
+     *           {@link PhoneAccountHandle} identifier.
+     * @param userHandle The {@link UserHandle} associated with this {@link PhoneAccountHandle}.
+     */
     public PhoneAccountHandle(
             @NonNull ComponentName componentName,
             @NonNull String id,
@@ -80,17 +106,17 @@
      * others supported by the connection service that created it.
      * <p>
      * A connection service must select identifiers that are stable for the lifetime of
-     * their users' relationship with their service, across many Android devices. For example, a
-     * good set of identifiers might be the email addresses with which with users registered for
-     * their accounts with a particular service. Depending on how a service chooses to operate,
-     * a bad set of identifiers might be an increasing series of integers
-     * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
-     * collide with values generated on other phones or after a data wipe of a given phone.
-     *
+     * their users' relationship with their service, across many Android devices.  The identifier
+     * should be a stable opaque token which uniquely identifies the user within the service.
+     * Depending on how a service chooses to operate, a bad set of identifiers might be an
+     * increasing series of integers ({@code 0}, {@code 1}, {@code 2}, ...) that are generated
+     * locally on each phone and could collide with values generated on other phones or after a data
+     * wipe of a given phone.
+     * <p>
      * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
      * behavior.
      *
-     * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
+     * @return A service-specific unique opaque identifier for this {@code PhoneAccountHandle}.
      */
     public String getId() {
         return mId;
@@ -157,7 +183,8 @@
         }
     }
 
-    public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR = new Creator<PhoneAccountHandle>() {
+    public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR =
+            new Creator<PhoneAccountHandle>() {
         @Override
         public PhoneAccountHandle createFromParcel(Parcel in) {
             return new PhoneAccountHandle(in);
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bf6a6ef7..efe35d2 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -239,13 +239,9 @@
                     conference.addConnection(c);
                 }
             }
-            if (conference.getConnections().size() == 0) {
-                // A conference was created, but none of its connections are ones that have been
-                // created by, and therefore being tracked by, this remote connection service. It
-                // is of no interest to us.
-                Log.d(this, "addConferenceCall - skipping");
-                return;
-            }
+            // We used to skip adding empty conferences; however in the world of IMS conference
+            // calls we need to add them to the remote connection service because they will always
+            // start with no participants.
 
             conference.setState(parcel.getState());
             conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
@@ -379,6 +375,8 @@
         @Override
         public void addExistingConnection(String callId, ParcelableConnection connection,
                 Session.Info sessionInfo) {
+            Log.i(RemoteConnectionService.this, "addExistingConnection: callId=%s, conn=%s", callId,
+                    connection);
             String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
                     getOpPackageName();
             int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
@@ -390,6 +388,20 @@
             Bundle newExtras = new Bundle();
             newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
                     connection.getPhoneAccount());
+            if (connection.getParentCallId() != null) {
+                RemoteConference parentConf = mConferenceById.get(connection.getParentCallId());
+                // If there is a parent being set, we need to stash the conference ID here.
+                // Telephony can add an existing connection while specifying a parent conference.
+                // There is no equivalent version of that operation as part of the remote connection
+                // API, so we will stash the pre-defined parent's ID in the extras.  When the
+                // connectionmanager copies over the extras from the remote connection to the
+                // actual one, it'll get passed to Telecom so that it can make the association.
+                if (parentConf != null) {
+                    newExtras.putString(Connection.EXTRA_ADD_TO_CONFERENCE_ID, parentConf.getId());
+                    Log.i(this, "addExistingConnection: stash parent of %s as %s",
+                            connection.getParentCallId(), parentConf.getId());
+                }
+            }
             remoteConnection.putExtras(newExtras);
             mConnectionById.put(callId, remoteConnection);
             remoteConnection.registerCallback(new RemoteConnection.Callback() {
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
index d585666..02d4eb3 100644
--- a/telephony/TEST_MAPPING
+++ b/telephony/TEST_MAPPING
@@ -23,6 +23,46 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
+    },
+    {
+      "name": "CtsTelephony2TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },    
+    {
+      "name": "CtsTelephonySdk28TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephony3TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsSimRestrictedApisTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephonyProviderTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ]
 }
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 1252965..3c799e0 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -26,7 +26,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.PersistableBundle;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.telephony.TelephonyManager;
 
@@ -74,11 +73,6 @@
         return cur == null ? Collections.emptyList() : cur;
     }
 
-    /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
-    public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
-        throw new RuntimeException(remoteException);
-    }
-
     /**
      * Returns a {@link ComponentInfo} from the {@link ResolveInfo},
      * or throws an {@link IllegalStateException} if not available.
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index a1c5bbe..ae597e0 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.RadioAccessSpecifier;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -76,13 +77,28 @@
      * Opportunistic network service will use these bands to scan.
      *
      * When no specific bands are specified (empty array or null) CBRS band
-     * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
+     * {@link AccessNetworkConstants.EutranBand.BAND_48
+     * } will be used for network scan.
      *
      * See {@link AccessNetworkConstants} for details.
+     *
+     * @deprecated use {@link #mRadioAccessSpecifiers} instead
      */
+    @Deprecated
     private ArrayList<Integer> mBands;
 
     /**
+     * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+     * Opportunistic network service will use this to determine which bands to scan for.
+     *
+     * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
+     * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
+     * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
+     * by Opportunistic network service.
+     */
+    private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
+
+    /**
      * Return subscription Id of the available network.
      * This value must be one of the entry retrieved from
      * {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -129,6 +145,22 @@
         return (List<Integer>) mBands.clone();
     }
 
+    /**
+     * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+     * Opportunistic network service will use this to determine which bands to scan for.
+     *
+     * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
+     * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
+     * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
+     * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
+     * by Opportunistic network service.
+     * @return the access network type associated with the available network.
+     * @hide
+     */
+    public List<RadioAccessSpecifier>  getRadioAccessSpecifiers() {
+        return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -140,6 +172,7 @@
         dest.writeInt(mPriority);
         dest.writeStringList(mMccMncs);
         dest.writeList(mBands);
+        dest.writeList(mRadioAccessSpecifiers);
     }
 
     private AvailableNetworkInfo(Parcel in) {
@@ -149,14 +182,25 @@
         in.readStringList(mMccMncs);
         mBands = new ArrayList<>();
         in.readList(mBands, Integer.class.getClassLoader());
+        mRadioAccessSpecifiers = new ArrayList<>();
+        in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader());
     }
 
     public AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
             @NonNull List<Integer> bands) {
+        this(subId, priority, mccMncs, bands,
+                new ArrayList<RadioAccessSpecifier>());
+    }
+
+    /** @hide */
+    private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
+            @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
+            radioAccessSpecifiers) {
         mSubId = subId;
         mPriority = priority;
         mMccMncs = new ArrayList<String>(mccMncs);
         mBands = new ArrayList<Integer>(bands);
+        mRadioAccessSpecifiers = new ArrayList<RadioAccessSpecifier>(radioAccessSpecifiers);
     }
 
     @Override
@@ -177,12 +221,13 @@
             && mPriority == ani.mPriority
             && (((mMccMncs != null)
             && mMccMncs.equals(ani.mMccMncs)))
-            && mBands.equals(ani.mBands));
+            && mBands.equals(ani.mBands))
+            && mRadioAccessSpecifiers.equals(ani.getRadioAccessSpecifiers());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSubId, mPriority, mMccMncs, mBands);
+        return Objects.hash(mSubId, mPriority, mMccMncs, mBands, mRadioAccessSpecifiers);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AvailableNetworkInfo> CREATOR =
@@ -204,6 +249,72 @@
             + " mSubId: " + mSubId
             + " mPriority: " + mPriority
             + " mMccMncs: " + Arrays.toString(mMccMncs.toArray())
-            + " mBands: " + Arrays.toString(mBands.toArray()));
+            + " mBands: " + Arrays.toString(mBands.toArray())
+            + " mRadioAccessSpecifiers: " + Arrays.toString(mRadioAccessSpecifiers.toArray()));
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link AvailableNetworkInfo} when
+     * creating a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code AvailableNetworkInfo}:
+     *
+     * <pre><code>
+     *
+     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
+     *     .setSubId(1)
+     *     .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private int mSubId = Integer.MIN_VALUE;
+        private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+        private ArrayList<String> mMccMncs = new ArrayList<>();
+        private ArrayList<Integer> mBands = new ArrayList<>();
+        private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
+
+        public @NonNull Builder setSubId(int subId) {
+            mSubId = subId;
+            return this;
+        }
+
+        public @NonNull Builder setPriority(int priority) {
+            if (priority > AvailableNetworkInfo.PRIORITY_LOW
+                    || priority < AvailableNetworkInfo.PRIORITY_HIGH) {
+                throw new IllegalArgumentException("A valid priority must be set");
+            }
+            mPriority = priority;
+            return this;
+        }
+
+        public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
+            Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
+                    + "list is still accepted. Please read documentation in "
+                    + "AvailableNetworkService to see consequences of an empty Arraylist.");
+            mMccMncs = mccMncs;
+            return this;
+        }
+
+        public @NonNull Builder setRadioAccessSpecifiers(
+                @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
+            Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
+                    + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
+                    + "read documentation in AvailableNetworkService to see consequences of an "
+                    + "empty Arraylist.");
+            mRadioAccessSpecifiers = radioAccessSpecifiers;
+            return this;
+        }
+
+        public @NonNull AvailableNetworkInfo build() {
+            if (mSubId == Integer.MIN_VALUE) {
+                throw new IllegalArgumentException("A valid subId must be set");
+            }
+
+            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+                    mRadioAccessSpecifiers);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f01519f..09e19d2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3744,6 +3744,109 @@
             "opportunistic_network_max_backoff_time_long";
 
     /**
+    * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered good
+    * enough for internet data.
+    *
+    * @hide
+    */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT =
+            "opportunistic_network_entry_threshold_ss_rsrp_int";
+
+    /**
+    * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered good
+    * enough for internet data.
+    *
+    * @hide
+    */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE =
+            "opportunistic_network_entry_threshold_ss_rsrq_double";
+
+    /**
+    * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not
+    * be considered good enough for internet data.
+    *
+    * @hide
+    */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT =
+            "opportunistic_network_exit_threshold_ss_rsrp_int";
+
+    /**
+    * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not
+    * be considered good enough for internet data.
+    *
+    * @hide
+    */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
+            "opportunistic_network_exit_threshold_ss_rsrq_double";
+
+    /**
+     * Controls back off time in milliseconds for switching back to
+     * 5G opportunistic subscription. This time will be added to
+     * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+     * determine hysteresis time if there is ping pong situation
+     * (determined by system app or 1st party app) between primary and 5G opportunistic
+     * subscription. Ping ping situation is defined in
+     * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG.
+     * If ping pong situation continuous #KEY_OPPORTUNISTIC_5G_NETWORK_BACKOFF_TIME_LONG
+     * will be added to previously determined hysteresis time.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG =
+            "opportunistic_network_5g_backoff_time_long";
+
+    /**
+     * Controls the max back off time in milliseconds for switching back to
+     * 5G opportunistic subscription.
+     * This time will be the max hysteresis that can be determined irrespective of there is
+     * continuous ping pong situation or not as described in
+     * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG and
+     * #KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG =
+            "opportunistic_network_5g_max_backoff_time_long";
+
+    /**
+    * Controls the ping pong determination of 5G opportunistic network.
+    * If opportunistic network is determined as out of service or below
+    * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT or
+    * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_INT within
+    * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG of switching to opportunistic network,
+    * it will be determined as ping pong situation by system app or 1st party app.
+     *
+    * @hide
+    */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG =
+            "opportunistic_network_5g_ping_pong_time_long";
+
+    /**
+     * Controls hysteresis time in milliseconds for which will be waited before switching
+     * data to a 5G opportunistic network.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG =
+            "opportunistic_network_5g_data_switch_hysteresis_time_long";
+
+    /**
+     * Controls hysteresis time in milliseconds for which will be waited before switching from
+     * 5G opportunistic network to primary network.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
+            "opportunistic_network_5g_data_switch_exit_hysteresis_time_long";
+    /**
+     * Controls whether 4G opportunistic networks should be scanned for possible data switch.
+     *
+     * @hide
+     */
+    public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
+            "enabled_4g_opportunistic_network_scan_bool";
+
+    /**
      * Indicates zero or more emergency number prefix(es), because some carrier requires
      * if users dial an emergency number address with a specific prefix, the combination of the
      * prefix and the address is also a valid emergency number to dial. For example, an emergency
@@ -5574,7 +5677,25 @@
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
         /* Default value is 60 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
-        sDefaults.putAll(ImsServiceEntitlement.getDefaults());
+        /* Default value is -111 dBm. */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT, -111);
+        /* Default value is -18.5 dB. */
+        sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+        /* Default value is -120 dBm. */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT, -120);
+        /* Default value is -18.5 dB. */
+        sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+        /* Default value is 10 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG, 10000);
+        /* Default value is 60 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG, 60000);
+        /* Default value is 60 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG, 60000);
+        /* Default value is 2 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000);
+        /* Default value is 2 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
+        sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d745dc2..a51b5c1 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1407,7 +1407,7 @@
         m.putString("data-operator-numeric", mOperatorNumeric);
         m.putBoolean("manual", mIsManualNetworkSelection);
         m.putInt("radioTechnology", getRilVoiceRadioTechnology());
-        m.putInt("dataRadioTechnology", getRadioTechnology());
+        m.putInt("dataRadioTechnology", getRilDataRadioTechnology());
         m.putBoolean("cssIndicator", mCssIndicator);
         m.putInt("networkId", mNetworkId);
         m.putInt("systemId", mSystemId);
@@ -1546,17 +1546,6 @@
     }
 
     /**
-     * @hide
-     * @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or
-     * {@link #getRilVoiceRadioTechnology}
-     */
-    @UnsupportedAppUsage
-    public int getRadioTechnology() {
-        Rlog.e(LOG_TAG, "ServiceState.getRadioTechnology() DEPRECATED will be removed *******");
-        return getRilDataRadioTechnology();
-    }
-
-    /**
      * Transform RIL radio technology {@link RilRadioTechnology} value to Network
      * type {@link NetworkType}.
      *
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5171cf9..73d1710 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2666,7 +2666,8 @@
      * @throws IllegalArgumentException if contentUri is empty
      */
     public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
-            @Nullable String locationUrl, @Nullable Bundle configOverrides,
+            @Nullable String locationUrl,
+            @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
             @Nullable PendingIntent sentIntent, long messageId) {
         if (contentUri == null) {
             throw new IllegalArgumentException("Uri contentUri null");
@@ -2742,7 +2743,8 @@
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
      */
     public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
-            @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+            @NonNull Uri contentUri,
+            @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
             @Nullable PendingIntent downloadedIntent, long messageId) {
         if (TextUtils.isEmpty(locationUrl)) {
             throw new IllegalArgumentException("Empty MMS location URL");
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 88c66ac..f4e2ade 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -79,7 +79,7 @@
      *   <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
      *   <li>The device is connected to the specific network which the carrier is using
      *   proprietary means to provide a faster overall data connection than would be otherwise
-     *   possible. This may include using other bands unique to the carrier, or carrier
+     *   possible.  This may include using other bands unique to the carrier, or carrier
      *   aggregation, for example.</li>
      * </ul>
      * One of the use case is that UX can show a different icon, for example, "5G+"
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 255a612..412c02c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8512,13 +8512,13 @@
 
     /**
      * Get the PLMN chosen for Manual Network Selection if active.
-     * Return null string if in automatic selection.
+     * Return empty string if in automatic selection.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
      * (see {@link #hasCarrierPrivileges})
      *
-     * @return manually selected network info on success or null string on failure
+     * @return manually selected network info on success or empty string on failure
      */
     @SuppressAutoDoc // No support carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -8531,7 +8531,7 @@
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
         }
-        return null;
+        return "";
     }
 
     /**
@@ -11115,14 +11115,10 @@
     @UnsupportedAppUsage
     public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) {
         int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                retval = service.getSubIdForPhoneAccount(phoneAccount);
-            }
-        } catch (RemoteException e) {
+        if (phoneAccount != null
+                && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+            retval = getSubscriptionId(phoneAccount.getAccountHandle());
         }
-
         return retval;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 018dabf..4fc7776 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1354,11 +1354,6 @@
             String callingFeatureId);
 
     /**
-     * Returns the subscription ID associated with the specified PhoneAccount.
-     */
-    int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
-
-    /**
      * Returns the subscription ID associated with the specified PhoneAccountHandle.
      */
     int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml
index 7633d52..ab5728e 100644
--- a/tests/BatteryStatsPerfTest/AndroidManifest.xml
+++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml
@@ -20,6 +20,8 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <service android:name="com.android.internal.os.BatteryUsageStatsPerfTest$BatteryUsageStatsService"
+                 android:process=":BatteryUsageStatsService" />
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 54d7047..2e4b6da 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -18,13 +18,25 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.Service;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.BatteryConsumer;
 import android.os.BatteryStatsManager;
 import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Binder;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.Parcel;
 import android.os.UidBatteryConsumer;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -54,7 +66,8 @@
 
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+            BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(
+                    new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build());
 
             state.pauseTiming();
 
@@ -71,4 +84,118 @@
             state.resumeTiming();
         }
     }
+
+    private final ConditionVariable mServiceConnected = new ConditionVariable();
+    private IBinder mService;
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = service;
+            mServiceConnected.open();
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+    };
+
+    /**
+     * Measures the performance of transferring BatteryUsageStats over a Binder.
+     */
+    @Test
+    public void testBatteryUsageStatsTransferOverBinder() throws Exception {
+        final Context context = InstrumentationRegistry.getContext();
+        context.bindService(
+                new Intent(context, BatteryUsageStatsService.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+        mServiceConnected.block(30000);
+        assertThat(mService).isNotNull();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            final Parcel data = Parcel.obtain();
+            final Parcel reply = Parcel.obtain();
+            mService.transact(42, data, reply, 0);
+            final BatteryUsageStats batteryUsageStats =
+                    BatteryUsageStats.CREATOR.createFromParcel(reply);
+            reply.recycle();
+            data.recycle();
+
+            state.pauseTiming();
+
+            assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+            assertThat(batteryUsageStats.getUidBatteryConsumers()).hasSize(1000);
+            final UidBatteryConsumer uidBatteryConsumer =
+                    batteryUsageStats.getUidBatteryConsumers().get(0);
+            assertThat(uidBatteryConsumer.getConsumedPower(1)).isEqualTo(123);
+
+            state.resumeTiming();
+        }
+
+        context.unbindService(mConnection);
+    }
+
+    /* This service runs in a separate process */
+    public static class BatteryUsageStatsService extends Service {
+        private final BatteryUsageStats mBatteryUsageStats;
+
+        public BatteryUsageStatsService() {
+            mBatteryUsageStats = buildBatteryUsageStats();
+        }
+
+        @Nullable
+        @Override
+        public IBinder onBind(Intent intent) {
+            return new Binder() {
+                @Override
+                protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+                        int flags) {
+                    mBatteryUsageStats.writeToParcel(reply, 0);
+                    return true;
+                }
+            };
+        }
+    }
+
+    private static BatteryUsageStats buildBatteryUsageStats() {
+        final BatteryUsageStats.Builder builder =
+                new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
+                        .setBatteryCapacity(4000)
+                        .setDischargePercentage(20)
+                        .setDischargedPowerRange(1000, 2000)
+                        .setStatsStartTimestamp(1000)
+                        .setStatsEndTimestamp(3000);
+
+        builder.getAggregateBatteryConsumerBuilder(
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+                .setConsumedPower(123)
+                .setConsumedPower(
+                        BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+                .setConsumedPowerForCustomComponent(
+                        BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+                .setUsageDurationMillis(
+                        BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+                .setUsageDurationForCustomComponentMillis(
+                        BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+
+        for (int i = 0; i < 1000; i++) {
+            final UidBatteryConsumer.Builder consumerBuilder =
+                    builder.getOrCreateUidBatteryConsumerBuilder(i)
+                            .setPackageWithHighestDrain("example.packagename" + i)
+                            .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+                            .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+            for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                    componentId++) {
+                consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+                        BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+                consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+            }
+
+            consumerBuilder.setConsumedPowerForCustomComponent(
+                    BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+                    .setUsageDurationForCustomComponentMillis(
+                            BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+        }
+        return builder.build();
+    }
 }
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
index 7ee0d9a..57303e7 100644
--- a/tests/BootImageProfileTest/OWNERS
+++ b/tests/BootImageProfileTest/OWNERS
@@ -1,4 +1,4 @@
 calin@google.com
-mathieuc@google.com
 ngeoffray@google.com
+vmarko@google.com
 yawanng@google.com
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 217a72b..7731e09 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -25,11 +25,17 @@
 
 android_test {
     name: "FlickerTests",
-    srcs: ["src/**/*.java", "src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     manifest: "AndroidManifest.xml",
     test_config: "AndroidTest.xml",
     platform_apis: true,
     certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
     test_suites: ["device-tests"],
     libs: ["android.test.runner"],
     static_libs: [
@@ -46,6 +52,9 @@
 java_library {
     name: "wm-flicker-common-assertions",
     platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
     srcs: [
         "src/**/*Assertions.java",
         "src/**/*Assertions.kt",
@@ -56,20 +65,23 @@
     static_libs: [
         "flickerlib",
         "truth-prebuilt",
-        "app-helpers-core"
+        "app-helpers-core",
     ],
 }
 
 java_library {
     name: "wm-flicker-common-app-helpers",
     platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
     srcs: [
-        "**/helpers/*"
+        "**/helpers/*",
     ],
     static_libs: [
         "flickerlib",
         "flickertestapplib",
         "truth-prebuilt",
-        "app-helpers-core"
+        "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 a540dff..08c9e5d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -14,77 +14,30 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonAssertions")
 package com.android.server.wm.flicker
 
-import android.platform.helpers.IAppHelper
+import android.content.ComponentName
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
-val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
+val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+        "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
 
-fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.statusBarWindowIsVisible() {
     assertWm {
-        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+        this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.navBarWindowIsVisible() {
     assertWm {
-        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
-    }
-}
-
-fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
-            .then()
-            .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesVisible() {
-    assertWm {
-        this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
-            .then()
-            .showsBelowAppWindow(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesInvisible() {
-    assertWm {
-        this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
-            .then()
-            .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) {
-    assertWm {
-        this.showsAppWindowOnTop(packageName)
-    }
-}
-
-fun FlickerTestParameter.appWindowBecomesVisible(appName: String) {
-    assertWm {
-        this.hidesAppWindow(appName)
-            .then()
-            .showsAppWindow(appName)
-    }
-}
-
-fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) {
-    assertWm {
-        this.showsAppWindow(appName)
-            .then()
-            .hidesAppWindow(appName)
+        this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
     }
 }
 
 @JvmOverloads
-fun FlickerTestParameter.noUncoveredRegions(
+fun FlickerTestParameter.entireScreenCovered(
     beginRotation: Int,
     endRotation: Int = beginRotation,
     allStates: Boolean = true
@@ -111,37 +64,21 @@
     }
 }
 
-@JvmOverloads
-fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(NAV_BAR_LAYER_NAME)
-                .then()
-                .isInvisible(NAV_BAR_LAYER_NAME)
-                .then()
-                .isVisible(NAV_BAR_LAYER_NAME)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(NAV_BAR_LAYER_NAME)
-        }
+fun FlickerTestParameter.navBarLayerIsVisible() {
+    assertLayersStart {
+        this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+    }
+    assertLayersEnd {
+        this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
     }
 }
 
-@JvmOverloads
-fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(STATUS_BAR_LAYER_NAME)
-                .then()
-                .isInvisible(STATUS_BAR_LAYER_NAME)
-                .then()
-                .isVisible(STATUS_BAR_LAYER_NAME)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(STATUS_BAR_LAYER_NAME)
-        }
+fun FlickerTestParameter.statusBarLayerIsVisible() {
+    assertLayersStart {
+        this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+    }
+    assertLayersEnd {
+        this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
     }
 }
 
@@ -154,10 +91,10 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     assertLayersStart {
-        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
+        this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
+        this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
     }
 }
 
@@ -170,54 +107,46 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     assertLayersStart {
-        this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(startingPos)
+        this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(endingPos)
+        this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
     }
 }
 
-fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
+/**
+ * Asserts that:
+ *     [originalLayer] is visible at the start of the trace
+ *     [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
+ *         becomes visible
+ *     [newLayer] remains visible until the end of the trace
+ *
+ * @param originalLayer Layer that should be visible at the start
+ * @param newLayer Layer that should be visible at the end
+ * @param ignoreSnapshot If the snapshot layer should be ignored during the transition
+ *     (useful mostly for app launch)
+ */
+fun FlickerTestParameter.replacesLayer(
+    originalLayer: ComponentName,
+    newLayer: ComponentName,
+    ignoreSnapshot: Boolean = false
+) {
     assertLayers {
-        this.isVisible(*HOME_WINDOW_TITLE)
-            .then()
-            .isVisible(appName)
+        val assertion = this.isVisible(originalLayer)
+        if (ignoreSnapshot) {
+            assertion.then()
+                    .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+        }
+        assertion.then().isVisible(newLayer)
+    }
+
+    assertLayersStart {
+        this.isVisible(originalLayer)
+                .isInvisible(newLayer)
+    }
+
+    assertLayersEnd {
+        this.isInvisible(originalLayer)
+                .isVisible(newLayer)
     }
 }
-
-fun FlickerTestParameter.launcherLayerReplacesApp(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-            .then()
-            .isInvisible(testApp.getPackage())
-            .isVisible(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
-    assertLayers {
-        this.isInvisible(packageName)
-            .then()
-            .isVisible(packageName)
-    }
-}
-
-fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
-    assertLayers {
-        this.isVisible(packageName)
-            .then()
-            .isInvisible(packageName)
-    }
-}
-
-fun FlickerTestParameter.focusChanges(vararg windows: String) {
-    assertEventLog {
-        this.focusChanges(windows)
-    }
-}
-
-fun FlickerTestParameter.focusDoesNotChange() {
-    assertEventLog {
-        this.focusDoesNotChange()
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 71184c2..90c851d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2020 The Android Open Source Project
  *
@@ -16,6 +17,8 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +26,7 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,6 +50,13 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @Postsubmit
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 6786279..e8391ed 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +25,7 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,6 +49,13 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @Postsubmit
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index f7f977d..f9e6bab 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,30 +19,35 @@
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherLayerReplacesApp
-import com.android.server.wm.flicker.launcherWindowBecomesVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.replacesLayer
 import org.junit.Test
 
+/**
+ * Base test class for transitions that close an app back to the launcher screen
+ */
 abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+    /**
+     * Specification of the test transition to execute
+     */
     protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
         setup {
             eachRun {
@@ -66,29 +71,29 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
+    }
+
+    @Presubmit
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -118,25 +123,33 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
     }
 
     @Presubmit
     @Test
     open fun launcherReplacesAppWindowAsTopWindow() {
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+                    .then()
+                    .isAppWindowOnTop(LAUNCHER_COMPONENT)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherWindowBecomesVisible() {
-        testSpec.launcherWindowBecomesVisible()
+        testSpec.assertWm {
+            this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowOnTop(LAUNCHER_COMPONENT)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherLayerReplacesApp() {
-        testSpec.launcherLayerReplacesApp(testApp)
+        testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 83fddae..d224af9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,8 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            wmHelper.waitImeWindowShown()
+            wmHelper.waitImeShown()
+            wmHelper.waitForAppTransitionIdle()
         }
     }
 
@@ -78,7 +79,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            wmHelper.waitImeWindowGone()
+            wmHelper.waitImeGone()
         }
     }
 }
\ No newline at end of file
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 b5757fd..384d8e8 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
@@ -18,8 +18,8 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,15 +28,15 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -46,6 +46,14 @@
 
 /**
  * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ *     Don't show if this is not explicitly requested by the user and the input method
+ *     is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
  */
 @RequiresDevice
@@ -79,37 +87,55 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
         }
     }
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+    fun imeAppWindowIsAlwaysVisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+    @Presubmit
+    @Test
+    fun imeLayerVisibleStart() {
+        testSpec.assertLayersStart {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun imeLayerInvisibleEnd() {
+        testSpec.assertLayersEnd {
+            this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
@@ -117,15 +143,19 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+    fun imeAppLayerIsAlwaysVisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+        }
+    }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
@@ -145,8 +175,11 @@
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
                 .getConfigNonRotationTests(repetitions = 5,
+                    // b/190352379 (IME doesn't show on app launch in 90 degrees)
+                    supportedRotations = listOf(Surface.ROTATION_0),
                     supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
                 )
         }
     }
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 549e44c..ade215b 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
@@ -30,14 +30,14 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -47,6 +47,14 @@
 
 /**
  * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ *     Don't show if this is not explicitly requested by the user and the input method
+ *     is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
  */
 @RequiresDevice
@@ -75,51 +83,73 @@
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
-                wmHelper.waitImeWindowGone()
+                wmHelper.waitImeGone()
             }
         }
     }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
         }
     }
 
-    @FlakyTest
+    @FlakyTest(bugId = 190189685)
     @Test
-    fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
-
-    @FlakyTest
-    @Test
-    fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+    fun imeAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+                .then()
+                .appWindowNotOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
-    @FlakyTest
+    @Presubmit
+    @Test
+    fun imeLayerVisibleStart() {
+        testSpec.assertLayersStart {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun imeLayerInvisibleEnd() {
+        testSpec.assertLayersEnd {
+            this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
     @Presubmit
     @Test
-    fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+    fun imeAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -133,18 +163,19 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+                    WindowManagerStateHelper.IME_COMPONENT,
+                    WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
         }
     }
 
@@ -154,8 +185,11 @@
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
                 .getConfigNonRotationTests(repetitions = 1,
+                    // b/190352379 (IME doesn't show on app launch in 90 degrees)
+                    supportedRotations = listOf(Surface.ROTATION_0),
                     supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
                 )
         }
     }
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 82ca074..cdfcff3 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
@@ -28,13 +28,13 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import org.junit.Assume
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
@@ -61,7 +61,7 @@
         return FlickerBuilder(instrumentation).apply {
             setup {
                 test {
-                    testApp.launchViaIntent()
+                    testApp.launchViaIntent(wmHelper)
                 }
                 eachRun {
                     testApp.openIME(device, wmHelper)
@@ -80,37 +80,42 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+                WindowManagerStateHelper.IME_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT))
         }
     }
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+    fun imeAppWindowIsAlwaysVisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
 
     @Presubmit
     @Test
@@ -146,7 +151,11 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+    fun imeAppLayerIsAlwaysVisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
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 703e4a1..05fc267 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
@@ -30,13 +30,13 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -68,7 +68,7 @@
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
-                wmHelper.waitImeWindowGone()
+                wmHelper.waitImeGone()
             }
             teardown {
                 eachRun {
@@ -84,19 +84,20 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+                WindowManagerStateHelper.IME_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT))
         }
     }
 
@@ -106,19 +107,25 @@
 
     @FlakyTest
     @Test
-    fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+    fun imeAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(testApp.component)
+                    .then()
+                    .isAppWindowInvisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
     @Presubmit
@@ -127,7 +134,13 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+    fun imeAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
@@ -144,8 +157,9 @@
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+                    WindowManagerStateHelper.IME_COMPONENT,
+                    WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7e34469..7659d94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -14,128 +14,56 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonAssertions")
 package com.android.server.wm.flicker.ime
 
-import android.platform.helpers.IAppHelper
 import com.android.server.wm.flicker.FlickerTestParameter
-
-const val IME_WINDOW_TITLE = "InputMethod"
-
-fun FlickerTestParameter.imeLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(IME_WINDOW_TITLE)
-                .then()
-                .isInvisible(IME_WINDOW_TITLE)
-                .then()
-                .isVisible(IME_WINDOW_TITLE)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(IME_WINDOW_TITLE)
-        }
-    }
-}
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 fun FlickerTestParameter.imeLayerBecomesVisible() {
     assertLayers {
-        this.isInvisible(IME_WINDOW_TITLE)
+        this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .isVisible(IME_WINDOW_TITLE)
+            .isVisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeLayerBecomesInvisible() {
     assertLayers {
-        this.isVisible(IME_WINDOW_TITLE)
+        this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .isInvisible(IME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
+            .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
     if (rotatesScreen) {
         assertWm {
-            this.showsNonAppWindow(IME_WINDOW_TITLE)
+            this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
                 .then()
-                .hidesNonAppWindow(IME_WINDOW_TITLE)
+                .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
                 .then()
-                .showsNonAppWindow(IME_WINDOW_TITLE)
+                .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
         }
     } else {
         assertWm {
-            this.showsNonAppWindow(IME_WINDOW_TITLE)
+            this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
         }
     }
 }
 
 fun FlickerTestParameter.imeWindowBecomesVisible() {
     assertWm {
-        this.hidesNonAppWindow(IME_WINDOW_TITLE)
+        this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .showsNonAppWindow(IME_WINDOW_TITLE)
+            .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeWindowBecomesInvisible() {
     assertWm {
-        this.showsNonAppWindow(IME_WINDOW_TITLE)
+        this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .hidesNonAppWindow(IME_WINDOW_TITLE)
+            .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(
-    testApp: IAppHelper,
-    rotatesScreen: Boolean = false
-) {
-    if (rotatesScreen) {
-        assertWm {
-            this.showsAppWindow(testApp.getPackage())
-                .then()
-                .hidesAppWindow(testApp.getPackage())
-                .then()
-                .showsAppWindow(testApp.getPackage())
-        }
-    } else {
-        assertWm {
-            this.showsAppWindow(testApp.getPackage())
-        }
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) {
-    assertWm {
-        this.hidesAppWindow(windowName)
-            .then()
-            .showsAppWindow(windowName)
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
-            .then()
-            .appWindowNotOnTop(testApp.getPackage())
-    }
-}
-
-fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-            .then()
-            .isInvisible(testApp.getPackage())
-    }
-}
\ No newline at end of file
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 cae1b16..f35a180 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
@@ -28,16 +28,15 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.appWindowAlwaysVisibleOnTop
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -81,11 +80,11 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
@@ -93,19 +92,23 @@
 
     @Presubmit
     @Test
-    fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+    fun appWindowAlwaysVisibleOnTop() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
 
     @Presubmit
     @Test
@@ -115,7 +118,7 @@
     @Test
     fun layerAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(testApp.`package`)
+            this.isVisible(testApp.component)
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index b7673d5..3bcf793 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
@@ -26,23 +27,22 @@
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,7 +61,6 @@
 class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
-    private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -73,14 +72,14 @@
                 }
                 eachRun {
                     device.pressRecentApps()
-                    wmHelper.waitImeWindowGone()
+                    wmHelper.waitImeGone()
                     wmHelper.waitForAppTransitionIdle()
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
             transitions {
                 device.reopenAppFromOverview(wmHelper)
-                wmHelper.waitImeWindowShown()
+                wmHelper.waitImeShown()
             }
             teardown {
                 test {
@@ -92,23 +91,34 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        val component = ComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+                    ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                            WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                            component)
+            )
         }
     }
 
     @Presubmit
     @Test
-    fun launcherWindowBecomesInvisible() = testSpec.launcherWindowBecomesInvisible()
+    fun launcherWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowInvisible(LAUNCHER_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
@@ -116,30 +126,57 @@
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp, true)
+    fun imeAppWindowVisibility() {
+        // the app starts visible in live tile, then becomes invisible during animation and
+        // is again launched. Since we log 1x per frame, sometimes the activity visibility and
+        // the app visibility are updated together, sometimes not, thus ignore activity check
+        // at the start
+        testSpec.assertWm {
+            this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+                    .then()
+                    .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+                    .then()
+                    .isAppWindowVisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
     // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun imeLayerIsAlwaysVisible() = testSpec.imeLayerIsAlwaysVisible(true)
+    fun imeLayerIsBecomesVisible() {
+        testSpec.assertLayers {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+                    .then()
+                    .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+                    .then()
+                    .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
-    fun appLayerReplacesLauncher() =
-        testSpec.appLayerReplacesLauncher(testAppComponentName.className)
+    fun appLayerReplacesLauncher() {
+        testSpec.assertLayers {
+            this.isVisible(LAUNCHER_COMPONENT)
+                .then()
+                .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+                .then()
+                .isVisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
@@ -156,8 +193,14 @@
     @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // depends on how much of the animation transactions are sent to SF at once
+        // sometimes this layer appears for 2-3 frames, sometimes for only 1
+        val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry()
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(
+                    listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                    WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+            )
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 0cae37c..f9dd88e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,27 +17,26 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
-
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -54,10 +53,11 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group2
+@Presubmit
 class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = SimpleAppHelper(instrumentation)
-    private val imeTestApp = ImeAppHelper(instrumentation)
+    private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -66,7 +66,13 @@
                 eachRun {
                     this.setRotation(testSpec.config.startRotation)
                     testApp.launchViaIntent(wmHelper)
+                    wmHelper.waitForFullScreenApp(testApp.component)
+                    wmHelper.waitForAppTransitionIdle()
+
                     imeTestApp.launchViaIntent(wmHelper)
+                    wmHelper.waitForFullScreenApp(testApp.component)
+                    wmHelper.waitForAppTransitionIdle()
+
                     imeTestApp.openIME(device, wmHelper)
                 }
             }
@@ -74,57 +80,86 @@
                 eachRun {
                     device.pressHome()
                     wmHelper.waitForHomeActivityVisible()
-                }
-                test {
-                    imeTestApp.exit(wmHelper)
+                    testApp.exit()
+                    imeTestApp.exit()
                 }
             }
             transitions {
                 // [Step1]: Swipe right from imeTestApp to testApp task
+                createTag(TAG_IME_VISIBLE)
                 val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-                val displayCenterX = displayBounds.bounds.width() / 2
-                device.swipe(displayCenterX, displayBounds.bounds.height(),
-                        displayBounds.bounds.width(), displayBounds.bounds.height(), 20)
+                device.swipe(0, displayBounds.bounds.height(),
+                        displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+
                 wmHelper.waitForFullScreenApp(testApp.component)
+                wmHelper.waitForAppTransitionIdle()
+                createTag(TAG_IME_INVISIBLE)
             }
             transitions {
                 // [Step2]: Swipe left to back to imeTestApp task
                 val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-                val displayCenterX = displayBounds.bounds.width() / 2
                 device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
-                        displayCenterX, displayBounds.bounds.height(), 20)
+                        0, displayBounds.bounds.height(), 50)
                 wmHelper.waitForFullScreenApp(imeTestApp.component)
             }
         }
     }
 
-    @FlakyTest
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(imeTestApp)
+    fun imeAppWindowVisibility() {
+        val component = ComponentName(imeTestApp.`package`, "")
+        testSpec.assertWm {
+            this.isAppWindowOnTop(component)
+                    .then()
+                    .isAppWindowVisible(component, ignoreActivity = true)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+    fun navBarLayerIsVisibleAroundSwitching() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+    fun statusBarLayerIsVisibleAroundSwitching() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
 
-    @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+        testSpec.assertLayersTag(TAG_IME_VISIBLE) {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
+        testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
+            isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
-    @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
-    @FlakyTest
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
@@ -134,10 +169,13 @@
                     .getConfigNonRotationTests(
                             repetitions = 3,
                             supportedNavigationModes = listOf(
-                                    WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
                                     WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                            )
+                            ),
+                            supportedRotations = listOf(Surface.ROTATION_0)
                     )
         }
+
+        private const val TAG_IME_VISIBLE = "imeVisible"
+        private const val TAG_IME_INVISIBLE = "imeInVisible"
     }
 }
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
deleted file mode 100644
index 01e34d9..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ /dev/null
@@ -1,29 +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 android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
-
-fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
-            .then()
-            .showsAppWindowOnTop("Snapshot", 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 9ff0bdf..e6dc852 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
@@ -64,35 +64,17 @@
 
     @FlakyTest
     @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-    }
-
-    @FlakyTest
-    @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
     override fun navBarLayerRotatesAndScales() {
         super.navBarLayerRotatesAndScales()
     }
 
-    @FlakyTest
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
+    @FlakyTest(bugId = 192721431)
     @Test
     override fun appLayerReplacesLauncher() {
         super.appLayerReplacesLauncher()
     }
 
-    @FlakyTest
+    @FlakyTest(bugId = 192721431)
     @Test
     override fun appWindowReplacesLauncherAsTopWindow() {
         super.appWindowReplacesLauncherAsTopWindow()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index b073a7c..7833e2f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -65,34 +65,10 @@
 
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
     override fun navBarLayerRotatesAndScales() {
         super.navBarLayerRotatesAndScales()
     }
 
-    @FlakyTest
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        super.statusBarLayerRotatesScales()
-    }
-
-    @FlakyTest
-    @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-    }
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index b304d5f..860a5ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -22,24 +22,24 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 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.navBarLayerIsVisible
 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.navBarWindowIsVisible
 import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.replacesLayer
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
 import org.junit.Test
 
 abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@@ -71,14 +71,14 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
     @Presubmit
@@ -89,14 +89,14 @@
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
     }
 
     @Presubmit
@@ -124,31 +124,43 @@
     @Presubmit
     @Test
     // During testing the launcher is always in portrait mode
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
     }
 
     @Presubmit
     @Test
     open fun focusChanges() {
-        testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+        testSpec.assertEventLog {
+            this.focusChanges("NexusLauncherActivity", testApp.`package`)
+        }
     }
 
     @Presubmit
     @Test
     open fun appLayerReplacesLauncher() {
-        testSpec.appLayerReplacesLauncher(testApp.`package`)
+        testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
     }
 
     @Presubmit
     @Test
     open fun appWindowReplacesLauncherAsTopWindow() {
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+        testSpec.assertWm {
+            this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+                    .then()
+                    .isAppWindowOnTop(testApp.component)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherWindowBecomesInvisible() {
-        testSpec.launcherWindowBecomesInvisible()
+        testSpec.assertWm {
+            this.isAppWindowVisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowInvisible(LAUNCHER_COMPONENT)
+        }
     }
 }
\ 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 e2705c7..b509c61 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
@@ -67,15 +67,7 @@
 
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    override fun navBarLayerRotatesAndScales() {
-        super.navBarLayerRotatesAndScales()
-    }
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
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 69e8a8d..73986b6 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
@@ -25,7 +25,13 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,39 +67,62 @@
         super.focusDoesNotChange()
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun screenshotLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.isVisible(testApp.getPackage())
+            this.isVisible(testApp.component)
                 .then()
-                .isVisible(SCREENSHOT_LAYER)
+                .isVisible(ROTATION_COMPONENT)
                 .then()
-                .isVisible(testApp.getPackage())
+                .isVisible(testApp.component)
         }
     }
 
-    @Postsubmit
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        super.statusBarLayerRotatesScales()
-    }
-
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() {
-        super.navBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(
+            testSpec.config.startRotation, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    override fun navBarWindowIsVisible() {
+        super.navBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
     }
 
     @FlakyTest
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
+    override fun navBarLayerRotatesAndScales() {
+        super.navBarLayerRotatesAndScales()
+    }
+
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
     companion object {
-        private const val SCREENSHOT_LAYER = "RotationLayer"
-
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 4b888cd..2b0b3c2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,25 +17,21 @@
 package com.android.server.wm.flicker.rotation
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 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.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 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.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.Test
 
@@ -69,19 +65,19 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(
@@ -90,31 +86,12 @@
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
-    }
-
-    @FlakyTest
-    @Test
-    open fun statusBarLayerRotatesScales() {
-        testSpec.statusBarLayerRotatesScales(
-            testSpec.config.startRotation, testSpec.config.endRotation)
-    }
-
-    @FlakyTest
-    @Test
     open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
             this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                    WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                    "SecondaryHomeHandle"
+                ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                    WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                    ComponentName("", "SecondaryHomeHandle")
                 )
             )
         }
@@ -130,22 +107,24 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(testSpec.config.startRotation,
             testSpec.config.endRotation, allStates = false)
     }
 
     @Presubmit
     @Test
     open fun focusDoesNotChange() {
-        testSpec.focusDoesNotChange()
+        testSpec.assertEventLog {
+            this.focusDoesNotChange()
+        }
     }
 
     @Presubmit
     @Test
     open fun appLayerRotates_StartingPos() {
         testSpec.assertLayersStart {
-            this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
+            this.visibleRegion(testApp.component).coversExactly(startingPos)
         }
     }
 
@@ -153,7 +132,7 @@
     @Test
     open fun appLayerRotates_EndingPos() {
         testSpec.assertLayersEnd {
-            this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
+            this.visibleRegion(testApp.component).coversExactly(endingPos)
         }
     }
 }
\ 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 b153bec..b97b977 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
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
+import android.view.WindowManager
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +28,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,42 +62,91 @@
             }
         }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() {
-        super.statusBarWindowIsAlwaysVisible()
+    fun appWindowFullScreen() {
+        testSpec.assertWm {
+            this.invoke("isFullScreen") {
+                val appWindow = it.windowState(testApp.`package`)
+                val flags = appWindow.windowState?.attributes?.flags ?: 0
+                appWindow.verify("isFullScreen")
+                    .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+                    .isGreaterThan(0)
+            }
+        }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
+    fun appWindowSeamlessRotation() {
+        testSpec.assertWm {
+            this.invoke("isRotationSeamless") {
+                val appWindow = it.windowState(testApp.`package`)
+                val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+                appWindow.verify("isRotationSeamless")
+                    .that(rotationAnimation
+                        .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
+                    .isGreaterThan(0)
+            }
+        }
     }
 
     @Presubmit
     @Test
     fun appLayerAlwaysVisible() {
         testSpec.assertLayers {
-            isVisible(testApp.`package`)
-        }
-    }
-
-    @FlakyTest(bugId = 185400889)
-    @Test
-    fun appLayerRotates() {
-        testSpec.assertLayers {
-            this.coversExactly(startingPos, testApp.`package`)
-                .then()
-                .coversExactly(endingPos, testApp.`package`)
+            isVisible(testApp.component)
         }
     }
 
     @Postsubmit
     @Test
+    fun appLayerRotates() {
+        testSpec.assertLayers {
+            this.coversExactly(startingPos, testApp.component)
+                .then()
+                .coversExactly(endingPos, testApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarWindowIsAlwaysInvisible() {
+        testSpec.assertWm {
+            this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarLayerIsAlwaysInvisible() {
+        testSpec.assertLayers {
+            this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsVisible() {
+        super.navBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
+    }
+
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
     companion object {
         private val testFactory = FlickerTestParameterFactory.getInstance()
 
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 4708cfd..c55e7c2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -23,5 +23,6 @@
     <EditText android:id="@+id/plain_text_input"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
+	      android:imeOptions="flagNoExtractUi"
               android:inputType="text"/>
 </LinearLayout>
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 2c6c8d7..1381c0a 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -73,6 +73,14 @@
             android:text="@string/play_trigger"
             android:onClick="onPlayTriggerButtonClicked"
             android:padding="20dp" />
+
+        <Button
+            android:id="@+id/get_state_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/get_model_state"
+            android:onClick="onGetModelStateButtonClicked"
+            android:padding="20dp" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index c48b648..adb0fcf 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -22,6 +22,7 @@
     <string name="start_recog">Start</string>
     <string name="stop_recog">Stop</string>
     <string name="play_trigger">Play Trigger Audio</string>
+    <string name="get_model_state">Get State</string>
     <string name="capture">Capture Audio</string>
     <string name="stop_capture">Stop Capturing Audio</string>
     <string name="play_capture">Play Captured Audio</string>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index c3c4cf5..72aa38d 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -257,6 +257,14 @@
         }
     }
 
+    public synchronized void onGetModelStateButtonClicked(View v) {
+        if (mService == null) {
+            Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService");
+        } else {
+            mService.getModelState(mSelectedModelUuid);
+        }
+    }
+
     public synchronized void onCaptureAudioCheckboxClicked(View v) {
         // See if we have the right permissions
         if (!mService.hasMicrophonePermission()) {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 380e299..6d4ffcf 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,6 +23,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioManager;
@@ -46,6 +48,7 @@
 import java.util.Random;
 import java.util.UUID;
 
+
 public class SoundTriggerTestService extends Service {
     private static final String TAG = "SoundTriggerTestSrv";
     private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
@@ -57,6 +60,8 @@
     private Random mRandom;
     private UserActivity mUserActivity;
 
+    private static int captureCount;
+
     public interface UserActivity {
         void addModel(UUID modelUuid, String state);
         void setModelState(UUID modelUuid, String state);
@@ -131,6 +136,8 @@
                         } else if (command.equals("set_capture_timeout")) {
                             setCaptureAudioTimeout(getModelUuidFromIntent(intent),
                                     intent.getIntExtra("timeout", 5000));
+                        } else if (command.equals("get_model_state")) {
+                            getModelState(getModelUuidFromIntent(intent));
                         } else {
                             Log.e(TAG, "Unknown command '" + command + "'");
                         }
@@ -432,6 +439,17 @@
         return modelInfo != null && modelInfo.captureAudioTrack != null;
     }
 
+    public synchronized void getModelState(UUID modelUuid) {
+        ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+        if (modelInfo == null) {
+            postError("Could not find model for: " + modelUuid.toString());
+            return;
+        }
+        int status = mSoundTriggerUtil.getModelState(modelUuid);
+        postMessage("GetModelState for: " + modelInfo.name + " returns: "
+            + status);
+    }
+
     private void loadModelsInDataDir() {
         // Load all the models in the data dir.
         boolean loadedModel = false;
@@ -527,18 +545,29 @@
         }
     }
 
+
     private class CaptureAudioRecorder implements Runnable {
         private final ModelInfo mModelInfo;
+
+        // EventPayload and RecognitionEvent are equivalant.  Only one will be non-null.
         private final SoundTriggerDetector.EventPayload mEvent;
+        private final RecognitionEvent mRecognitionEvent;
 
         public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
             mModelInfo = modelInfo;
             mEvent = event;
+            mRecognitionEvent = null;
+        }
+
+        public CaptureAudioRecorder(ModelInfo modelInfo, RecognitionEvent event) {
+            mModelInfo = modelInfo;
+            mEvent = null;
+            mRecognitionEvent = event;
         }
 
         @Override
         public void run() {
-            AudioFormat format = mEvent.getCaptureAudioFormat();
+            AudioFormat format = getAudioFormat();
             if (format == null) {
                 postErrorToast("No audio format in recognition event.");
                 return;
@@ -600,18 +629,21 @@
                 }
 
                 audioRecord = new AudioRecord(attributes, format, bytesRequired,
-                        mEvent.getCaptureSession());
+                        getCaptureSession());
 
                 byte[] buffer = new byte[bytesRequired];
 
                 // Create a file so we can save the output data there for analysis later.
                 FileOutputStream fos  = null;
                 try {
-                    fos = new FileOutputStream( new File(
-                            getFilesDir() + File.separator
-                                    + mModelInfo.name.replace(' ', '_')
-                                    + "_capture_" + format.getChannelCount() + "ch_"
-                                    + format.getSampleRate() + "hz_" + encoding + ".pcm"));
+                    File file = new File(
+                        getFilesDir() + File.separator
+                        + mModelInfo.name.replace(' ', '_')
+                        + "_capture_" + format.getChannelCount() + "ch_"
+                        + format.getSampleRate() + "hz_" + encoding
+                        + "_" + (++captureCount) + ".pcm");
+                    Log.i(TAG, "Writing audio to: " + file);
+                    fos = new FileOutputStream(file);
                 } catch (IOException e) {
                     Log.e(TAG, "Failed to open output for saving PCM data", e);
                     postErrorToast("Failed to open output for saving PCM data: "
@@ -635,6 +667,10 @@
                     bytesRequired -= bytesRead;
                 }
                 audioRecord.stop();
+                if (fos != null) {
+                  fos.flush();
+                  fos.close();
+                }
             } catch (Exception e) {
                 Log.e(TAG, "Error recording trigger audio", e);
                 postErrorToast("Error recording trigger audio: " + e.getMessage());
@@ -651,6 +687,26 @@
                 setModelState(mModelInfo, "Recording finished");
             }
         }
+
+        private AudioFormat getAudioFormat() {
+            if (mEvent != null) {
+                return mEvent.getCaptureAudioFormat();
+            }
+            if (mRecognitionEvent != null) {
+                return mRecognitionEvent.captureFormat;
+            }
+            return null;
+        }
+
+        private int getCaptureSession() {
+            if (mEvent != null) {
+                return mEvent.getCaptureSession();
+            }
+            if (mRecognitionEvent != null) {
+                return mRecognitionEvent.captureSession;
+            }
+            return 0;
+        }
     }
 
     // Implementation of SoundTriggerDetector.Callback.
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index cfe8c85..996a78f 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,6 +18,8 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.media.soundtrigger.SoundTriggerDetector;
 import android.media.soundtrigger.SoundTriggerManager;
 import android.os.RemoteException;
@@ -27,6 +29,7 @@
 
 import com.android.internal.app.ISoundTriggerService;
 
+import java.lang.reflect.Method;
 import java.lang.RuntimeException;
 import java.util.UUID;
 
@@ -50,13 +53,31 @@
      * The sound model must contain a valid UUID.
      *
      * @param soundModel The sound model to add/update.
+     * @return The true if the model was loaded successfully, false otherwise.
      */
     public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
         if (soundModel == null) {
             throw new RuntimeException("Bad sound model");
         }
         mSoundTriggerManager.updateModel(soundModel);
-        return true;
+        // TODO: call loadSoundModel in the soundtrigger manager updateModel method
+        // instead of here. It is needed to keep soundtrigger manager internal
+        // state consistent.
+        return mSoundTriggerManager
+                .loadSoundModel(getGenericSoundModel(soundModel)) == 0;
+    }
+
+    private GenericSoundModel getGenericSoundModel(
+        SoundTriggerManager.Model soundModel) {
+        try {
+            Method method = SoundTriggerManager.Model.class
+                            .getDeclaredMethod("getGenericSoundModel");
+            method.setAccessible(true);
+            return (GenericSoundModel) method.invoke(soundModel);
+        } catch (ReflectiveOperationException e) {
+            Log.e(TAG, "Failed to getGenericSoundModel: " + soundModel, e);
+            return null;
+        }
     }
 
     /**
@@ -92,6 +113,16 @@
         return true;
     }
 
+    /**
+     * Get the current model state
+     *
+     * @param modelId The model ID to look-up the sound model for.
+     * @return 0 if the call succeeds, or an error code if it fails.
+     */
+    public int getModelState(UUID modelId) {
+        return mSoundTriggerManager.getModelState(modelId);
+    }
+
     public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
             SoundTriggerDetector.Callback callback) {
         return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index e07fbbf..dc090aa 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,15 +37,19 @@
         "vts",
     ],
     data: [
-        ":NotoColorEmojiTtf",
+        ":NotoColorEmoji.ttf",
+        ":NotoSerif-Regular.ttf",
+        ":NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTestCertDer",
-        ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
-        ":UpdatableSystemFontTestNotoColorEmojiV0Ttf",
-        ":UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
-        ":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
-        ":UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
-        ":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
-        ":UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+        ":UpdatableSystemFontTest_NotoColorEmoji.sig",
+        ":UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+        ":UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+        ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+        ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
+        ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+        ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
     ],
     sdk_version: "test_current",
 }
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 4f6487e..6effa7b 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -29,13 +29,17 @@
         <option name="cleanup" value="true" />
         <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
         <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf" />
-        <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig" />
+        <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
+        <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
index 947e9c2..a8c27fb0 100644
--- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
+++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
@@ -20,6 +20,7 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import android.app.Activity;
+import android.graphics.Typeface;
 import android.os.Bundle;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -27,14 +28,20 @@
 /** Test app to render an emoji. */
 public class EmojiRenderingTestActivity extends Activity {
 
+    private static final String TEST_NOTO_SERIF = "test-noto-serif";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         LinearLayout container = new LinearLayout(this);
         container.setOrientation(LinearLayout.VERTICAL);
-        TextView textView = new TextView(this);
-        textView.setText("\uD83E\uDD72"); // 🥲
-        container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+        TextView emojiTextView = new TextView(this);
+        emojiTextView.setText("\uD83E\uDD72"); // 🥲
+        container.addView(emojiTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+        TextView serifTextView = new TextView(this);
+        serifTextView.setTypeface(Typeface.create(TEST_NOTO_SERIF, Typeface.NORMAL));
+        serifTextView.setText(TEST_NOTO_SERIF);
+        container.addView(serifTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
         setContentView(container);
     }
 }
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6bd07d0..87fda0d 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -16,6 +16,9 @@
 
 package com.android.updatablesystemfont;
 
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_BOLD;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +33,7 @@
 import android.graphics.fonts.FontFamilyUpdateRequest;
 import android.graphics.fonts.FontFileUpdateRequest;
 import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontStyle;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.RootPermissionTest;
 import android.security.FileIntegrityManager;
@@ -77,31 +81,45 @@
     private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
     private static final String DATA_FONTS_DIR = "/data/fonts/files/";
     private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
-    private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
 
-    private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+    private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
+    private static final String NOTO_COLOR_EMOJI_TTF =
             "/data/local/tmp/NotoColorEmoji.ttf";
-    private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+    private static final String NOTO_COLOR_EMOJI_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
     // A font with revision == 0.
     private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf";
-    private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf";
+    private static final String TEST_NOTO_COLOR_EMOJI_V0_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig";
     // A font with revision == original + 1
     private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf";
-    private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf";
+    private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig";
     // A font with revision == original + 2
     private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf";
-    private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
-            "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf";
+    private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig";
+
+    private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
+    private static final String NOTO_SERIF_REGULAR_TTF =
+            "/data/local/tmp/NotoSerif-Regular.ttf";
+    private static final String NOTO_SERIF_REGULAR_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
+
+    private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
+    private static final String NOTO_SERIF_BOLD_TTF =
+            "/data/local/tmp/NotoSerif-Bold.ttf";
+    private static final String NOTO_SERIF_BOLD_SIG =
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
 
     private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
     private static final String EMOJI_RENDERING_TEST_ACTIVITY =
             EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
+    // This should be the same as the one in EmojiRenderingTestActivity.
+    private static final String TEST_NOTO_SERIF = "test-noto-serif";
     private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
 
     private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY =
@@ -141,11 +159,20 @@
 
     @Test
     public void updateFont() throws Exception {
+        FontConfig oldFontConfig =
+                SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
+        // Check that font config is updated.
         String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(fontPath).startsWith(DATA_FONTS_DIR);
+        FontConfig newFontConfig =
+                SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
+        assertThat(newFontConfig.getConfigVersion())
+                .isGreaterThan(oldFontConfig.getConfigVersion());
+        assertThat(newFontConfig.getLastModifiedTimeMillis())
+                .isGreaterThan(oldFontConfig.getLastModifiedTimeMillis());
         // The updated font should be readable and unmodifiable.
         expectCommandToSucceed("dd status=none if=" + fontPath + " of=/dev/null");
         expectCommandToFail("dd status=none if=" + CERT_PATH + " of=" + fontPath);
@@ -154,11 +181,11 @@
     @Test
     public void updateFont_twice() throws Exception {
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
@@ -173,16 +200,16 @@
     public void updateFont_allowSameVersion() throws Exception {
         // Update original font to the same version
         assertThat(updateFontFile(
-                ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG))
+                NOTO_COLOR_EMOJI_TTF, NOTO_COLOR_EMOJI_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         // Update updated font to the same version
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -195,28 +222,58 @@
     @Test
     public void updateFont_invalidCert() throws Exception {
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
                 .isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
     }
 
     @Test
     public void updateFont_downgradeFromSystem() throws Exception {
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_SIG))
                 .isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
     }
 
     @Test
     public void updateFont_downgradeFromData() throws Exception {
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
     }
 
     @Test
+    public void updateFontFamily() throws Exception {
+        assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
+        FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+        assertThat(family.getFontList()).hasSize(2);
+        assertThat(family.getFontList().get(0).getPostScriptName())
+                .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+        assertThat(family.getFontList().get(0).getFile().getAbsolutePath())
+                .startsWith(DATA_FONTS_DIR);
+        assertThat(family.getFontList().get(0).getStyle().getWeight())
+                .isEqualTo(FONT_WEIGHT_NORMAL);
+        assertThat(family.getFontList().get(1).getPostScriptName())
+                .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+        assertThat(family.getFontList().get(1).getFile().getAbsolutePath())
+                .startsWith(DATA_FONTS_DIR);
+        assertThat(family.getFontList().get(1).getStyle().getWeight()).isEqualTo(FONT_WEIGHT_BOLD);
+    }
+
+    @Test
+    public void updateFontFamily_asNewFont() throws Exception {
+        assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
+                .isEqualTo(FontManager.RESULT_SUCCESS);
+        FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+        assertThat(family.getFontList()).hasSize(2);
+        assertThat(family.getFontList().get(0).getPostScriptName())
+                .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+        assertThat(family.getFontList().get(1).getPostScriptName())
+                .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+    }
+
+    @Test
     public void launchApp() throws Exception {
         String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
@@ -231,22 +288,25 @@
         String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
         assertThat(updateFontFile(
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                 .isEqualTo(FontManager.RESULT_SUCCESS);
         String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
+        updateNotoSerifAs(TEST_NOTO_SERIF);
+        String notoSerifPath = getFontPath(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
         startActivity(EMOJI_RENDERING_TEST_APP_ID, EMOJI_RENDERING_TEST_ACTIVITY);
         // The original font should NOT be opened by the app.
         SystemUtil.eventually(() -> {
             assertThat(isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
             assertThat(isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
+            assertThat(isFileOpenedBy(notoSerifPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
         }, ACTIVITY_TIMEOUT_MILLIS);
     }
 
     @Test
     public void reboot() throws Exception {
         expectCommandToSucceed(String.format("cmd font update %s %s",
-                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+                TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
         String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
         assertThat(fontPath).startsWith(DATA_FONTS_DIR);
 
@@ -264,7 +324,7 @@
                 Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
         for (int i = 0; i < 10; i++) {
             assertThat(updateFontFile(
-                    TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+                    TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
                     .isEqualTo(FontManager.RESULT_SUCCESS);
             List<String> openFiles = getOpenFiles("system_server");
             for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
@@ -285,7 +345,7 @@
     public void fdLeakTest_withoutPermission() throws Exception {
         Pattern patternEmojiVPlus1 =
                 Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
-        byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+        byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
         try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
                 new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
             assertThrows(SecurityException.class,
@@ -340,18 +400,56 @@
                 configVersion);
     }
 
+    private int updateNotoSerifAs(String familyName) throws IOException {
+        List<FontFamilyUpdateRequest.Font> fonts = Arrays.asList(
+                new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME,
+                        new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT)).build(),
+                new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_BOLD_POSTSCRIPT_NAME,
+                        new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT)).build());
+        FontFamilyUpdateRequest.FontFamily fontFamily =
+                new FontFamilyUpdateRequest.FontFamily.Builder(familyName, fonts).build();
+        byte[] regularSig = Files.readAllBytes(Paths.get(NOTO_SERIF_REGULAR_SIG));
+        byte[] boldSig = Files.readAllBytes(Paths.get(NOTO_SERIF_BOLD_SIG));
+        try (ParcelFileDescriptor regularFd = ParcelFileDescriptor.open(
+                    new File(NOTO_SERIF_REGULAR_TTF), MODE_READ_ONLY);
+             ParcelFileDescriptor boldFd = ParcelFileDescriptor.open(
+                    new File(NOTO_SERIF_BOLD_TTF), MODE_READ_ONLY)) {
+            return SystemUtil.runWithShellPermissionIdentity(() -> {
+                FontConfig fontConfig = mFontManager.getFontConfig();
+                return mFontManager.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+                        .addFontFileUpdateRequest(
+                                new FontFileUpdateRequest(regularFd, regularSig))
+                        .addFontFileUpdateRequest(
+                                new FontFileUpdateRequest(boldFd, boldSig))
+                        .addFontFamily(fontFamily)
+                        .build(), fontConfig.getConfigVersion());
+            });
+        }
+    }
+
     private String getFontPath(String psName) {
-        return SystemUtil.runWithShellPermissionIdentity(() -> {
-            FontConfig fontConfig = mFontManager.getFontConfig();
-            for (FontConfig.FontFamily family : fontConfig.getFontFamilies()) {
-                for (FontConfig.Font font : family.getFontList()) {
-                    if (psName.equals(font.getPostScriptName())) {
-                        return font.getFile().getAbsolutePath();
-                    }
-                }
-            }
-            throw new AssertionError("Font not found: " + psName);
-        });
+        FontConfig fontConfig =
+                SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+        return fontConfig.getFontFamilies().stream()
+                .flatMap(family -> family.getFontList().stream())
+                .filter(font -> psName.equals(font.getPostScriptName()))
+                // Return the last match, because the latter family takes precedence if two families
+                // have the same name.
+                .reduce((first, second) -> second)
+                .orElseThrow(() -> new AssertionError("Font not found: " + psName))
+                .getFile()
+                .getAbsolutePath();
+    }
+
+    private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+        FontConfig fontConfig =
+                SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+        return fontConfig.getFontFamilies().stream()
+                .filter(family -> familyName.equals(family.getName()))
+                // Return the last match, because the latter family takes precedence if two families
+                // have the same name.
+                .reduce((first, second) -> second)
+                .orElseThrow(() -> new AssertionError("Family not found: " + familyName));
     }
 
     private static void startActivity(String appId, String activityId) throws Exception {
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 426464e..5eba336 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -48,9 +48,9 @@
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiV0Ttf",
-    srcs: [":NotoColorEmojiTtf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf"],
+    name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+    srcs: [":NotoColorEmoji.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
     tools: ["update_font_metadata"],
     cmd: "$(location update_font_metadata) " +
         "--input=$(in) " +
@@ -59,9 +59,9 @@
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
-    srcs: [":NotoColorEmojiTtf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf"],
+    name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+    srcs: [":NotoColorEmoji.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
     tools: ["update_font_metadata"],
     cmd: "$(location update_font_metadata) " +
         "--input=$(in) " +
@@ -70,9 +70,9 @@
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
-    srcs: [":NotoColorEmojiTtf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf"],
+    name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+    srcs: [":NotoColorEmoji.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
     tools: ["update_font_metadata"],
     cmd: "$(location update_font_metadata) " +
         "--input=$(in) " +
@@ -94,29 +94,43 @@
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+    name: "UpdatableSystemFontTest_NotoColorEmoji.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoColorEmojiTtf"],
-    out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+    srcs: [":NotoColorEmoji.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmoji.sig"],
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
+    name: "UpdatableSystemFontTest_NotoColorEmojiV0.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":UpdatableSystemFontTestNotoColorEmojiV0Ttf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig"],
+    srcs: [":UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiV0.sig"],
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
+    name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig"],
+    srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig"],
 }
 
 genrule {
-    name: "UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+    name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf"],
-    out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"],
+    srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
+    out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig"],
+}
+
+genrule {
+    name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
+    defaults: ["updatable_system_font_sig_gen_default"],
+    srcs: [":NotoSerif-Regular.ttf"],
+    out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
+}
+
+genrule {
+    name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
+    defaults: ["updatable_system_font_sig_gen_default"],
+    srcs: [":NotoSerif-Bold.ttf"],
+    out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
 }
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 bcd6ed7..824f91e 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
@@ -45,6 +45,7 @@
             // Test specifications for FrameworksMockingCoreTests.
             "android.app.activity.ActivityThreadClientTest",
             "android.view.DisplayTest",
+            "android.window.ConfigurationHelperTest",
             // Test specifications for FrameworksCoreTests.
             "android.app.servertransaction.", // all tests under the package.
             "android.view.CutoutSpecificationTest",
@@ -59,10 +60,8 @@
             "android.view.RoundedCornersTest",
             "android.view.WindowMetricsTest",
             "android.view.PendingInsetsControllerTest",
-            "android.window.WindowContextTest",
-            "android.window.WindowMetricsHelperTest",
+            "android.window.", // all tests under the package.
             "android.app.activity.ActivityThreadTest",
-            "android.window.WindowContextControllerTest"
     };
 
     public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -3,5 +3,5 @@
 benedictwong@google.com
 ckesting@google.com
 evitayan@google.com
+junyin@google.com
 nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
index 72db55b..e9026e2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -87,8 +87,8 @@
                         eq(mVcnNetworkProvider),
                         argThat(
                                 score ->
-                                        score.getLegacyInt()
-                                                == Vcn.getNetworkScore().getLegacyInt()),
+                                        score.getLegacyInt() == Vcn.getNetworkScore().getLegacyInt()
+                                                && score.isTransportPrimary()),
                         any(),
                         any(),
                         cbCaptor.capture());
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index d3ca357..cabbe7e 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,11 +17,10 @@
 #ifndef AAPT_APP_INFO_H
 #define AAPT_APP_INFO_H
 
+#include <optional>
 #include <set>
 #include <string>
 
-#include "util/Maybe.h"
-
 namespace aapt {
 
 // Information relevant to building an app, parsed from the app's AndroidManifest.xml.
@@ -30,19 +29,19 @@
   std::string package;
 
   // The app's minimum SDK version, if it is defined.
-  Maybe<int> min_sdk_version;
+  std::optional<int> min_sdk_version;
 
   // The app's version code (the lower 32 bits of the long version code), if it is defined.
-  Maybe<uint32_t> version_code;
+  std::optional<uint32_t> version_code;
 
   // The app's version code major (the upper 32 bits of the long version code), if it is defined.
-  Maybe<uint32_t> version_code_major;
+  std::optional<uint32_t> version_code_major;
 
   // The app's revision code, if it is defined.
-  Maybe<uint32_t> revision_code;
+  std::optional<uint32_t> revision_code;
 
   // The app's split name, if it is a split.
-  Maybe<std::string> split_name;
+  std::optional<std::string> split_name;
 
   // The split names that this split depends on.
   std::set<std::string> split_name_dependencies;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index ef3a62f..df444ba 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -280,8 +280,7 @@
       printer->Indent();
       for (const ResourceTableEntryView& entry : type.entries) {
         printer->Print("resource ");
-        printer->Print(ResourceId(package.id.value_or_default(0), type.id.value_or_default(0),
-                                  entry.id.value_or_default(0))
+        printer->Print(ResourceId(package.id.value_or(0), type.id.value_or(0), entry.id.value_or(0))
                            .to_string());
         printer->Print(" ");
 
@@ -362,7 +361,7 @@
       continue;
     }
 
-    Maybe<ResourceTable::SearchResult> result = table->FindResource(style_name);
+    std::optional<ResourceTable::SearchResult> result = table->FindResource(style_name);
     if (result) {
       ResourceEntry* entry = result.value().entry;
       for (const auto& value : entry->values) {
@@ -482,8 +481,7 @@
 
       if (attr.compiled_attribute) {
         printer_->Print("(");
-        printer_->Print(
-            attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
+        printer_->Print(attr.compiled_attribute.value().id.value_or(ResourceId(0)).to_string());
         printer_->Print(")");
       }
       printer_->Print("=");
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 8a43bb4..b249c6c 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -147,7 +147,7 @@
  private:
   io::FileOutputStream* out_;
   IDiagnostics* diagnostics_;
-  Maybe<std::string> trace_folder_;
+  std::optional<std::string> trace_folder_;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index f1aad29..0b49052 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -21,7 +21,6 @@
 #include <string>
 
 #include "Resource.h"
-#include "util/Maybe.h"
 
 namespace aapt {
 
@@ -44,7 +43,7 @@
  public:
   explicit NameMangler(NameManglerPolicy policy) : policy_(policy) {}
 
-  Maybe<ResourceName> MangleName(const ResourceName& name) {
+  std::optional<ResourceName> MangleName(const ResourceName& name) {
     if (policy_.target_package_name == name.package ||
         policy_.packages_to_mangle.count(name.package) == 0) {
       return {};
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index f1e2da9..f49c254 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -20,7 +20,8 @@
 #include <limits>
 #include <sstream>
 
-#include "android-base/logging.h"
+#include <android-base/logging.h>
+#include <idmap2/Policies.h>
 
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
@@ -28,12 +29,10 @@
 #include "ValueVisitor.h"
 #include "text/Utf8Iterator.h"
 #include "util/ImmutableMap.h"
-#include "util/Maybe.h"
+
 #include "util/Util.h"
 #include "xml/XmlPullParser.h"
 
-#include "idmap2/Policies.h"
-
 using ::aapt::ResourceUtils::StringBuilder;
 using ::aapt::text::Utf8Iterator;
 using ::android::ConfigDescription;
@@ -109,8 +108,8 @@
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool staged_api = false;
   bool allow_new = false;
-  Maybe<OverlayableItem> overlayable_item;
-  Maybe<StagedId> staged_alias;
+  std::optional<OverlayableItem> overlayable_item;
+  std::optional<StagedId> staged_alias;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -252,7 +251,7 @@
   std::string current_text;
 
   // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
-  Maybe<size_t> untranslatable_start_depth;
+  std::optional<size_t> untranslatable_start_depth;
 
   Node root;
   std::vector<Node*> node_stack;
@@ -342,7 +341,7 @@
         }
 
         node_stack.pop_back();
-        if (untranslatable_start_depth == make_value(depth)) {
+        if (untranslatable_start_depth == depth) {
           // This is the end of an untranslatable section.
           untranslatable_start_depth = {};
         }
@@ -468,7 +467,7 @@
     }
 
     // Extract the product name if it exists.
-    if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
+    if (std::optional<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
       parsed_resource.product = maybe_product.value().to_string();
     }
 
@@ -560,7 +559,7 @@
     resource_format = android::ResTable_map::TYPE_ANY;
 
     // Items have their type encoded in the type attribute.
-    if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+    if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
       resource_type = maybe_type.value().to_string();
     } else {
       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -568,7 +567,7 @@
       return false;
     }
 
-    if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
+    if (std::optional<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
       // An explicit format for this resource was specified. The resource will
       // retain its type in its name, but the accepted value for this type is
       // overridden.
@@ -584,7 +583,7 @@
     can_be_item = false;
 
     // Bags have their type encoded in the type attribute.
-    if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+    if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
       resource_type = maybe_type.value().to_string();
     } else {
       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -595,7 +594,7 @@
 
   // Get the name of the resource. This will be checked later, because not all
   // XML elements require a name.
-  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+  std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
 
   if (resource_type == "id") {
     if (!maybe_name) {
@@ -835,10 +834,8 @@
 bool ResourceParser::ParseString(xml::XmlPullParser* parser,
                                  ParsedResource* out_resource) {
   bool formatted = true;
-  if (Maybe<StringPiece> formatted_attr =
-          xml::FindAttribute(parser, "formatted")) {
-    Maybe<bool> maybe_formatted =
-        ResourceUtils::ParseBool(formatted_attr.value());
+  if (std::optional<StringPiece> formatted_attr = xml::FindAttribute(parser, "formatted")) {
+    std::optional<bool> maybe_formatted = ResourceUtils::ParseBool(formatted_attr.value());
     if (!maybe_formatted) {
       diag_->Error(DiagMessage(out_resource->source)
                    << "invalid value for 'formatted'. Must be a boolean");
@@ -848,8 +845,8 @@
   }
 
   bool translatable = options_.translatable;
-  if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
-    Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+  if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+    std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
     if (!maybe_translatable) {
       diag_->Error(DiagMessage(out_resource->source)
                    << "invalid value for 'translatable'. Must be a boolean");
@@ -929,7 +926,7 @@
                 << "ignoring configuration '" << out_resource->config << "' for <public> tag");
   }
 
-  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+  std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
   if (!maybe_type) {
     diag_->Error(DiagMessage(out_resource->source)
                  << "<public> must have a 'type' attribute");
@@ -946,8 +943,8 @@
 
   out_resource->name.type = *parsed_type;
 
-  if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
-    Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+  if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
+    std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
     if (!maybe_id) {
       diag_->Error(DiagMessage(out_resource->source)
                    << "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
@@ -974,7 +971,7 @@
                << "> tag");
   }
 
-  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+  std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
   if (!maybe_type) {
     diag->Error(DiagMessage(out_resource->source)
                 << "<" << tag_name << "> must have a 'type' attribute");
@@ -988,14 +985,14 @@
     return false;
   }
 
-  Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
+  std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
   if (!maybe_id_str) {
     diag->Error(DiagMessage(out_resource->source)
                 << "<" << tag_name << "> must have a 'first-id' attribute");
     return false;
   }
 
-  Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+  std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
   if (!maybe_id) {
     diag->Error(DiagMessage(out_resource->source)
                 << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
@@ -1090,7 +1087,7 @@
 
 bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
                                      ParsedResource* out_resource) {
-  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+  std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
   if (!maybe_type) {
     diag_->Error(DiagMessage(out_resource->source)
                  << "<" << parser->element_name()
@@ -1137,7 +1134,7 @@
                 << "' for <overlayable> tag");
   }
 
-  Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+  std::optional<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
   if (!overlayable_name) {
     diag_->Error(DiagMessage(out_resource->source)
                   << "<overlayable> tag must have a 'name' attribute");
@@ -1146,7 +1143,7 @@
 
   const std::string kActorUriScheme =
       android::base::StringPrintf("%s://", Overlayable::kActorScheme);
-  Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+  std::optional<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
   if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
     diag_->Error(DiagMessage(out_resource->source)
                  << "specified <overlayable> tag 'actor' attribute must use the scheme '"
@@ -1194,7 +1191,7 @@
       }
 
       // Items specify the name and type of resource that should be overlayable
-      Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+      std::optional<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!item_name) {
         diag_->Error(DiagMessage(element_source)
                      << "<item> within an <overlayable> must have a 'name' attribute");
@@ -1202,7 +1199,7 @@
         continue;
       }
 
-      Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+      std::optional<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
       if (!item_type) {
         diag_->Error(DiagMessage(element_source)
                      << "<item> within an <overlayable> must have a 'type' attribute");
@@ -1236,7 +1233,8 @@
         diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
         error = true;
         break;
-      } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+      } else if (std::optional<StringPiece> maybe_type =
+                     xml::FindNonEmptyAttribute(parser, "type")) {
         // Parse the polices separated by vertical bar characters to allow for specifying multiple
         // policies. Items within the policy tag will have the specified policy.
         for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
@@ -1302,7 +1300,7 @@
 
   uint32_t type_mask = 0;
 
-  Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
+  std::optional<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
   if (maybe_format) {
     type_mask = ParseFormatAttribute(maybe_format.value());
     if (type_mask == 0) {
@@ -1312,9 +1310,9 @@
     }
   }
 
-  Maybe<int32_t> maybe_min, maybe_max;
+  std::optional<int32_t> maybe_min, maybe_max;
 
-  if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
+  if (std::optional<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
     StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
     if (!min_str.empty()) {
       std::u16string min_str16 = util::Utf8ToUtf16(min_str);
@@ -1331,7 +1329,7 @@
     }
   }
 
-  if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
+  if (std::optional<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
     StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
     if (!max_str.empty()) {
       std::u16string max_str16 = util::Utf8ToUtf16(max_str);
@@ -1398,8 +1396,7 @@
         type_mask |= android::ResTable_map::TYPE_FLAGS;
       }
 
-      if (Maybe<Attribute::Symbol> s =
-              ParseEnumOrFlagItem(parser, element_name)) {
+      if (std::optional<Attribute::Symbol> s = ParseEnumOrFlagItem(parser, element_name)) {
         Attribute::Symbol& symbol = s.value();
         ParsedResource child_resource;
         child_resource.name = symbol.symbol.name.value();
@@ -1443,24 +1440,24 @@
       type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
   attr->SetWeak(weak);
   attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
-  attr->min_int = maybe_min.value_or_default(std::numeric_limits<int32_t>::min());
-  attr->max_int = maybe_max.value_or_default(std::numeric_limits<int32_t>::max());
+  attr->min_int = maybe_min.value_or(std::numeric_limits<int32_t>::min());
+  attr->max_int = maybe_max.value_or(std::numeric_limits<int32_t>::max());
   out_resource->value = std::move(attr);
   return true;
 }
 
-Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
-    xml::XmlPullParser* parser, const StringPiece& tag) {
+std::optional<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+                                                                     const StringPiece& tag) {
   const Source source = source_.WithLine(parser->line_number());
 
-  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+  std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   if (!maybe_name) {
     diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
                                      << tag << ">");
     return {};
   }
 
-  Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
+  std::optional<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
   if (!maybe_value) {
     diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
                                      << tag << ">");
@@ -1484,13 +1481,13 @@
 bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
   const Source source = source_.WithLine(parser->line_number());
 
-  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+  std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   if (!maybe_name) {
     diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
     return false;
   }
 
-  Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+  std::optional<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
   if (!maybe_key) {
     diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
     return false;
@@ -1515,7 +1512,7 @@
 
   std::unique_ptr<Style> style = util::make_unique<Style>();
 
-  Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
+  std::optional<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
   if (maybe_parent) {
     // If the parent is empty, we don't have a parent, but we also don't infer either.
     if (!maybe_parent.value().empty()) {
@@ -1571,7 +1568,7 @@
 
 bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   uint32_t resource_format = android::ResTable_map::TYPE_ANY;
-  if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
+  if (std::optional<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
     resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
     if (resource_format == 0u) {
       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -1598,8 +1595,8 @@
   std::unique_ptr<Array> array = util::make_unique<Array>();
 
   bool translatable = options_.translatable;
-  if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
-    Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+  if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+    std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
     if (!maybe_translatable) {
       diag_->Error(DiagMessage(out_resource->source)
                    << "invalid value for 'translatable'. Must be a boolean");
@@ -1664,8 +1661,7 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "item") {
-      Maybe<StringPiece> maybe_quantity =
-          xml::FindNonEmptyAttribute(parser, "quantity");
+      std::optional<StringPiece> maybe_quantity = xml::FindNonEmptyAttribute(parser, "quantity");
       if (!maybe_quantity) {
         diag_->Error(DiagMessage(item_source)
                      << "<item> in <plurals> requires attribute "
@@ -1767,7 +1763,7 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "attr") {
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+      std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!maybe_name) {
         diag_->Error(DiagMessage(item_source) << "<attr> tag must have a 'name' attribute");
         error = true;
@@ -1777,7 +1773,7 @@
       // If this is a declaration, the package name may be in the name. Separate
       // these out.
       // Eg. <attr name="android:text" />
-      Maybe<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+      std::optional<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
       if (!maybe_ref) {
         diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
                                               << maybe_name.value() << "'");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 2614997..548f5f9 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -18,6 +18,7 @@
 #define AAPT_RESOURCE_PARSER_H
 
 #include <memory>
+#include <optional>
 
 #include "android-base/macros.h"
 #include "androidfw/ConfigDescription.h"
@@ -27,7 +28,6 @@
 #include "ResourceTable.h"
 #include "ResourceValues.h"
 #include "StringPool.h"
-#include "util/Maybe.h"
 #include "xml/XmlPullParser.h"
 
 namespace aapt {
@@ -54,7 +54,7 @@
 
   // If visibility was forced, we need to use it when creating a new resource and also error if we
   // try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
-  Maybe<Visibility::Level> visibility;
+  std::optional<Visibility::Level> visibility;
 };
 
 struct FlattenedXmlSubTree {
@@ -122,8 +122,8 @@
   bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
-  Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
-                                               const android::StringPiece& tag);
+  std::optional<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+                                                       const android::StringPiece& tag);
   bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
   bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 279ebcba..556ffa22 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -567,12 +567,12 @@
   Style* style = test::GetValue<Style>(&table_, "style/foo");
   ASSERT_THAT(style, NotNull());
   ASSERT_TRUE(style->parent);
-  EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu"))));
+  EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/fu")));
   ASSERT_THAT(style->entries, SizeIs(3));
 
-  EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
-  EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
-  EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+  EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("attr/bar")));
+  EXPECT_THAT(style->entries[1].key.name, Eq(test::ParseNameOrDie("attr/bat")));
+  EXPECT_THAT(style->entries[2].key.name, Eq(test::ParseNameOrDie("attr/baz")));
 }
 
 TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
@@ -581,7 +581,7 @@
   Style* style = test::GetValue<Style>(&table_, "style/foo");
   ASSERT_THAT(style, NotNull());
   ASSERT_TRUE(style->parent);
-  EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme"))));
+  EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("com.app:style/Theme")));
 }
 
 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
@@ -594,7 +594,7 @@
   ASSERT_THAT(style, NotNull());
   ASSERT_TRUE(style->parent);
   ASSERT_TRUE(style->parent.value().name);
-  EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme"))));
+  EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("android:style/Theme")));
 }
 
 TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
@@ -607,7 +607,7 @@
   Style* style = test::GetValue<Style>(&table_, "style/foo");
   ASSERT_THAT(style, NotNull());
   ASSERT_THAT(style->entries, SizeIs(1));
-  EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
+  EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("android:attr/bar")));
 }
 
 TEST_F(ResourceParserTest, ParseStyleWithRawStringItem) {
@@ -634,7 +634,7 @@
   Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
   ASSERT_THAT(style, NotNull());
   ASSERT_TRUE(style->parent);
-  EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo"))));
+  EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/foo")));
   EXPECT_TRUE(style->parent_inferred);
 }
 
@@ -672,7 +672,7 @@
       </declare-styleable>)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> result =
+  std::optional<ResourceTable::SearchResult> result =
       table_.FindResource(test::ParseNameOrDie("styleable/foo"));
   ASSERT_TRUE(result);
   EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
@@ -695,9 +695,9 @@
   ASSERT_THAT(styleable, NotNull());
   ASSERT_THAT(styleable->entries, SizeIs(3));
 
-  EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
-  EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
-  EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+  EXPECT_THAT(styleable->entries[0].name, Eq(test::ParseNameOrDie("attr/bar")));
+  EXPECT_THAT(styleable->entries[1].name, Eq(test::ParseNameOrDie("attr/bat")));
+  EXPECT_THAT(styleable->entries[2].name, Eq(test::ParseNameOrDie("attr/baz")));
 }
 
 TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) {
@@ -913,7 +913,8 @@
       </public-group>)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+  std::optional<ResourceTable::SearchResult> result =
+      table_.FindResource(test::ParseNameOrDie("attr/foo"));
   ASSERT_TRUE(result);
   ASSERT_TRUE(result.value().entry->id);
   EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010040)));
@@ -932,7 +933,8 @@
       </staging-public-group>)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+  std::optional<ResourceTable::SearchResult> result =
+      table_.FindResource(test::ParseNameOrDie("attr/foo"));
   ASSERT_TRUE(result);
 
   ASSERT_TRUE(result.value().entry->id);
@@ -959,7 +961,7 @@
       <java-symbol type="string" name="foo" />)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> result =
+  std::optional<ResourceTable::SearchResult> result =
       table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(result);
 
@@ -977,7 +979,7 @@
 TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
   ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)"));
 
-  Maybe<ResourceTable::SearchResult> result =
+  std::optional<ResourceTable::SearchResult> result =
       table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(result);
   const ResourceEntry* entry = result.value().entry;
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8ab1493..ad014a2 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <tuple>
 
 #include "android-base/logging.h"
@@ -68,7 +69,7 @@
 
 template <typename T, typename U>
 bool less_than_struct_with_name_and_id(const T& lhs,
-                                       const std::pair<std::string_view, Maybe<U>>& rhs) {
+                                       const std::pair<std::string_view, std::optional<U>>& rhs) {
   if (lhs.id != rhs.second) {
     return lhs.id < rhs.second;
   }
@@ -341,20 +342,20 @@
 
 void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
                               const ResourceTableType* type, const std::string& entry_name,
-                              const Maybe<ResourceId>& id, const Visibility& visibility,
-                              const Maybe<AllowNew>& allow_new,
-                              const Maybe<OverlayableItem>& overlayable_item,
-                              const Maybe<StagedId>& staged_id,
+                              const std::optional<ResourceId>& id, const Visibility& visibility,
+                              const std::optional<AllowNew>& allow_new,
+                              const std::optional<OverlayableItem>& overlayable_item,
+                              const std::optional<StagedId>& staged_id,
                               const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
   SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
   SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
   SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
 
   ResourceTablePackageView new_package{package->name,
-                                       id ? id.value().package_id() : Maybe<uint8_t>{}};
+                                       id ? id.value().package_id() : std::optional<uint8_t>{}};
   auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
 
-  ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe<uint8_t>{}};
+  ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : std::optional<uint8_t>{}};
   auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
 
   if (visibility.level == Visibility::Level::kPublic) {
@@ -363,7 +364,7 @@
   }
 
   ResourceTableEntryView new_entry{.name = entry_name,
-                                   .id = id ? id.value().entry_id() : Maybe<uint16_t>{},
+                                   .id = id ? id.value().entry_id() : std::optional<uint16_t>{},
                                    .visibility = visibility,
                                    .allow_new = allow_new,
                                    .overlayable_item = overlayable_item,
@@ -585,7 +586,8 @@
   return true;
 }
 
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(
+    const ResourceNameRef& name) const {
   ResourceTablePackage* package = FindPackage(name.package);
   if (package == nullptr) {
     return {};
@@ -603,8 +605,8 @@
   return SearchResult{package, type, entry};
 }
 
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
-                                                               ResourceId id) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
+                                                                       ResourceId id) const {
   ResourceTablePackage* package = FindPackage(name.package);
   if (package == nullptr) {
     return {};
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index bae1d82..2e17659 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -120,18 +120,18 @@
   const std::string name;
 
   // The entry ID for this resource (the EEEE in 0xPPTTEEEE).
-  Maybe<ResourceId> id;
+  std::optional<ResourceId> id;
 
   // Whether this resource is public (and must maintain the same entry ID across builds).
   Visibility visibility;
 
-  Maybe<AllowNew> allow_new;
+  std::optional<AllowNew> allow_new;
 
   // The declarations of this resource as overlayable for RROs
-  Maybe<OverlayableItem> overlayable_item;
+  std::optional<OverlayableItem> overlayable_item;
 
   // The staged resource id for a finalized resource.
-  Maybe<StagedId> staged_id;
+  std::optional<StagedId> staged_id;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -205,11 +205,11 @@
 
 struct ResourceTableEntryView {
   std::string name;
-  Maybe<uint16_t> id;
+  std::optional<uint16_t> id;
   Visibility visibility;
-  Maybe<AllowNew> allow_new;
-  Maybe<OverlayableItem> overlayable_item;
-  Maybe<StagedId> staged_id;
+  std::optional<AllowNew> allow_new;
+  std::optional<OverlayableItem> overlayable_item;
+  std::optional<StagedId> staged_id;
   std::vector<const ResourceConfigValue*> values;
 
   const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
@@ -218,7 +218,7 @@
 
 struct ResourceTableTypeView {
   ResourceType type;
-  Maybe<uint8_t> id;
+  std::optional<uint8_t> id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
 
   // Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
@@ -228,7 +228,7 @@
 
 struct ResourceTablePackageView {
   std::string name;
-  Maybe<uint8_t> id;
+  std::optional<uint8_t> id;
   // Types sorted in ascending type id order. If ids have not been assigned, the types are sorted by
   // their declaration order in the ResourceType enum.
   std::vector<ResourceTableTypeView> types;
@@ -309,8 +309,8 @@
     ResourceEntry* entry;
   };
 
-  Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
-  Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
+  std::optional<SearchResult> FindResource(const ResourceNameRef& name) const;
+  std::optional<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
   bool RemoveResource(const ResourceNameRef& name, ResourceId id) const;
 
   // Returns the package struct with the given name, or nullptr if such a package does not
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 38391c9..de73d2c 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -162,7 +162,7 @@
   EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
   EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
 
-  Maybe<ResourceTable::SearchResult> sr =
+  std::optional<ResourceTable::SearchResult> sr =
       table.FindResource(test::ParseNameOrDie("android:string/foo"));
   ASSERT_TRUE(sr);
   std::vector<ResourceConfigValue*> values =
@@ -187,7 +187,7 @@
                                                        const ResourceNameRef& name,
                                                        Visibility::Level level,
                                                        const StringPiece& comment) {
-  Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
+  std::optional<ResourceTable::SearchResult> result = table.FindResource(name);
   if (!result) {
     return ::testing::AssertionFailure() << "no resource '" << name << "' found in table";
   }
@@ -242,7 +242,7 @@
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
   AllowNew allow_new;
-  Maybe<ResourceTable::SearchResult> result;
+  std::optional<ResourceTable::SearchResult> result;
 
   allow_new.comment = "first";
   ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetAllowNew(allow_new).Build(),
@@ -274,7 +274,7 @@
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
   ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
                                 test::GetDiagnostics()));
-  Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
+  std::optional<ResourceTable::SearchResult> search_result = table.FindResource(name);
 
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index e0e80ac..ead06bf 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -40,8 +40,7 @@
 namespace aapt {
 namespace ResourceUtils {
 
-Maybe<ResourceName> ToResourceName(
-    const android::ResTable::resource_name& name_in) {
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name_in) {
   // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
   ResourceName name_out;
   if (!name_in.package) {
@@ -78,7 +77,7 @@
   return name_out;
 }
 
-Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
   ResourceName name_out;
   if (!name_in.package) {
     return {};
@@ -251,8 +250,7 @@
  * <[*]package>:[style/]<entry>
  * [[*]package:style/]<entry>
  */
-Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
-                                           std::string* out_error) {
+std::optional<Reference> ParseStyleParentReference(const StringPiece& str, std::string* out_error) {
   if (str.empty()) {
     return {};
   }
@@ -301,7 +299,7 @@
   return result;
 }
 
-Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
+std::optional<Reference> ParseXmlAttributeName(const StringPiece& str) {
   StringPiece trimmed_str = util::TrimWhitespace(str);
   const char* start = trimmed_str.data();
   const char* const end = start + trimmed_str.size();
@@ -326,7 +324,7 @@
   }
 
   ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
-  return Maybe<Reference>(std::move(ref));
+  return std::optional<Reference>(std::move(ref));
 }
 
 std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
@@ -488,18 +486,18 @@
                : util::make_unique<BinaryPrimitive>(value);
 }
 
-Maybe<bool> ParseBool(const StringPiece& str) {
+std::optional<bool> ParseBool(const StringPiece& str) {
   StringPiece trimmed_str(util::TrimWhitespace(str));
   if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
-    return Maybe<bool>(true);
+    return std::optional<bool>(true);
   } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
              trimmed_str == "False") {
-    return Maybe<bool>(false);
+    return std::optional<bool>(false);
   }
   return {};
 }
 
-Maybe<uint32_t> ParseInt(const StringPiece& str) {
+std::optional<uint32_t> ParseInt(const StringPiece& str) {
   std::u16string str16 = util::Utf8ToUtf16(str);
   android::Res_value value;
   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
@@ -508,7 +506,7 @@
   return {};
 }
 
-Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
+std::optional<ResourceId> ParseResourceId(const StringPiece& str) {
   StringPiece trimmed_str(util::TrimWhitespace(str));
 
   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -524,7 +522,7 @@
   return {};
 }
 
-Maybe<int> ParseSdkVersion(const StringPiece& str) {
+std::optional<int> ParseSdkVersion(const StringPiece& str) {
   StringPiece trimmed_str(util::TrimWhitespace(str));
 
   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -534,7 +532,7 @@
   }
 
   // Try parsing the code name.
-  Maybe<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
+  std::optional<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
   if (entry) {
     return entry.value();
   }
@@ -551,7 +549,7 @@
 }
 
 std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
-  if (Maybe<bool> maybe_result = ParseBool(str)) {
+  if (std::optional<bool> maybe_result = ParseBool(str)) {
     const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
     return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
   }
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index be493db..fe450a8 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -75,35 +75,33 @@
 /**
  * Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
  */
-Maybe<ResourceName> ToResourceName(
-    const android::ResTable::resource_name& name);
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name);
 
 /**
  * Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
  */
-Maybe<ResourceName> ToResourceName(
-    const android::AssetManager2::ResourceName& name_in);
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in);
 
 /**
  * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
  * false, or False.
  */
-Maybe<bool> ParseBool(const android::StringPiece& str);
+std::optional<bool> ParseBool(const android::StringPiece& str);
 
 /**
  * Returns a uint32_t if the string is an integer.
  */
-Maybe<uint32_t> ParseInt(const android::StringPiece& str);
+std::optional<uint32_t> ParseInt(const android::StringPiece& str);
 
 /**
  * Returns an ID if it the string represented a valid ID.
  */
-Maybe<ResourceId> ParseResourceId(const android::StringPiece& str);
+std::optional<ResourceId> ParseResourceId(const android::StringPiece& str);
 
 /**
  * Parses an SDK version, which can be an integer, or a letter from A-Z.
  */
-Maybe<int> ParseSdkVersion(const android::StringPiece& str);
+std::optional<int> ParseSdkVersion(const android::StringPiece& str);
 
 /*
  * Returns a Reference, or None Maybe instance if the string `str` was parsed as
@@ -116,7 +114,8 @@
  * ?[package:]style/<entry> or
  * <package>:[style/]<entry>
  */
-Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std::string* out_error);
+std::optional<Reference> ParseStyleParentReference(const android::StringPiece& str,
+                                                   std::string* out_error);
 
 /*
  * Returns a Reference if the string `str` was parsed as a valid XML attribute
@@ -125,7 +124,7 @@
  *
  * package:entry
  */
-Maybe<Reference> ParseXmlAttributeName(const android::StringPiece& str);
+std::optional<Reference> ParseXmlAttributeName(const android::StringPiece& str);
 
 /*
  * Returns a Reference object if the string was parsed as a resource or
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index b08bf9a..1aaa34d 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -30,15 +30,15 @@
 namespace aapt {
 
 TEST(ResourceUtilsTest, ParseBool) {
-  EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(Maybe<bool>(true)));
-  EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(Maybe<bool>(true)));
-  EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(Maybe<bool>(true)));
+  EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(std::optional<bool>(true)));
+  EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(std::optional<bool>(true)));
+  EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(std::optional<bool>(true)));
 
-  EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(Maybe<bool>(false)));
-  EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(Maybe<bool>(false)));
-  EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(Maybe<bool>(false)));
+  EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(std::optional<bool>(false)));
+  EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(std::optional<bool>(false)));
+  EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(std::optional<bool>(false)));
 
-  EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(Maybe<bool>(false)));
+  EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(std::optional<bool>(false)));
 }
 
 TEST(ResourceUtilsTest, ParseResourceName) {
@@ -155,41 +155,42 @@
   const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
 
   std::string err_str;
-  Maybe<Reference> ref = ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
+  std::optional<Reference> ref =
+      ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
 
   ref = ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
   ASSERT_TRUE(ref);
-  EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+  EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
   EXPECT_TRUE(ref.value().private_reference);
 }
 
@@ -228,15 +229,11 @@
 }
 
 TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
-  EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(Maybe<int>(10000)));
-  EXPECT_THAT(
-      ResourceUtils::ParseSdkVersion("Q.fingerprint"),
-      Eq(Maybe<int>(10000)));
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(std::optional<int>(10000)));
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q.fingerprint"), Eq(std::optional<int>(10000)));
 
-  EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(Maybe<int>(10000)));
-  EXPECT_THAT(
-      ResourceUtils::ParseSdkVersion("R.fingerprint"),
-      Eq(Maybe<int>(10000)));
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(std::optional<int>(10000)));
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion("R.fingerprint"), Eq(std::optional<int>(10000)));
 }
 
 TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 2a90f26..b3ab4ff 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -120,7 +120,7 @@
     return false;
   }
 
-  const ResourceId resid = id.value_or_default(ResourceId(0));
+  const ResourceId resid = id.value_or(ResourceId(0));
   const bool dynamic = resid.is_valid() && is_dynamic;
 
   if (reference_type == Reference::Type::kResource) {
@@ -1040,7 +1040,7 @@
 }
 
 bool operator<(const Reference& a, const Reference& b) {
-  int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
+  int cmp = a.name.value_or(ResourceName{}).compare(b.name.value_or(ResourceName{}));
   if (cmp != 0) return cmp < 0;
   return a.id < b.id;
 }
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index d903b7e..1694d6b 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -31,7 +31,6 @@
 #include "ValueTransformer.h"
 #include "io/File.h"
 #include "text/Printer.h"
-#include "util/Maybe.h"
 
 namespace aapt {
 
@@ -159,8 +158,8 @@
     kAttribute,
   };
 
-  Maybe<ResourceName> name;
-  Maybe<ResourceId> id;
+  std::optional<ResourceName> name;
+  std::optional<ResourceId> id;
   std::optional<uint32_t> type_flags;
   Reference::Type reference_type;
   bool private_reference = false;
@@ -327,7 +326,7 @@
     friend std::ostream& operator<<(std::ostream& out, const Entry& entry);
   };
 
-  Maybe<Reference> parent;
+  std::optional<Reference> parent;
 
   // If set to true, the parent was auto inferred from the style's name.
   bool parent_inferred = false;
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 96f6512..f7a5ba1 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
 
 static ApiVersion sDevelopmentSdkLevel = 10000;
 static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
-    "Q", "R", "S"
+    "Q", "R", "S", "T"
 });
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
@@ -77,9 +77,10 @@
   return iter->second;
 }
 
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
   return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
-      ? Maybe<ApiVersion>() : sDevelopmentSdkLevel;
+             ? std::optional<ApiVersion>()
+             : sDevelopmentSdkLevel;
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 6bb6ddb..7518e70 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -60,7 +60,7 @@
 };
 
 ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 92934c3..4f9369a 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -17,21 +17,20 @@
 #ifndef AAPT_SOURCE_H
 #define AAPT_SOURCE_H
 
+#include <optional>
 #include <ostream>
 #include <string>
 
 #include "android-base/stringprintf.h"
 #include "androidfw/StringPiece.h"
 
-#include "util/Maybe.h"
-
 namespace aapt {
 
 // Represents a file on disk. Used for logging and showing errors.
 struct Source {
   std::string path;
-  Maybe<size_t> line;
-  Maybe<std::string> archive;
+  std::optional<size_t> line;
+  std::optional<std::string> archive;
 
   Source() = default;
 
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 919b4c9..b1452fa 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -72,7 +72,7 @@
 }
 
 void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description,
-                              Maybe<std::string>* value, uint32_t flags) {
+                              std::optional<std::string>* value, uint32_t flags) {
   auto func = [value, flags](const StringPiece& arg) -> bool {
     *value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
     return true;
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index d21571d..8678cda 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -18,6 +18,7 @@
 #define AAPT_COMMAND_H
 
 #include <functional>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <unordered_set>
@@ -25,19 +26,20 @@
 
 #include "androidfw/StringPiece.h"
 
-#include "util/Maybe.h"
-
 namespace aapt {
 
 class Command {
  public:
-  explicit Command(const android::StringPiece& name) : name_(name.to_string()),
-                                                       short_name_(""),
-                                                       full_subcommand_name_(name.to_string()) {}
+  explicit Command(const android::StringPiece& name)
+      : name_(name.to_string()), full_subcommand_name_(name.to_string()){};
 
   explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
-      : name_(name.to_string()), short_name_(short_name.to_string()),
-        full_subcommand_name_(name.to_string()) {}
+      : name_(name.to_string()),
+        short_name_(short_name.to_string()),
+        full_subcommand_name_(name.to_string()){};
+
+  Command(Command&&) = default;
+  Command& operator=(Command&&) = default;
 
   virtual ~Command() = default;
 
@@ -58,7 +60,7 @@
                            uint32_t flags = 0);
 
   void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
-                       Maybe<std::string>* value, uint32_t flags = 0);
+                       std::optional<std::string>* value, uint32_t flags = 0);
 
   void AddOptionalFlagList(const android::StringPiece& name,
                            const android::StringPiece& description, std::vector<std::string>* value,
@@ -87,8 +89,6 @@
   virtual int Action(const std::vector<std::string>& args) = 0;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(Command);
-
   struct Flag {
     explicit Flag(const android::StringPiece& name, const android::StringPiece& description,
                   const bool is_required, const size_t num_args,
@@ -104,8 +104,8 @@
     bool found = false;
   };
 
-  const std::string name_;
-  const std::string short_name_;
+  std::string name_;
+  std::string short_name_;
   std::string description_ = "";
   std::string full_subcommand_name_;
 
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 65608fd..7aa1aa01 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -38,7 +38,7 @@
   TestCommand command;
   std::string required_flag;
   command.AddRequiredFlag("--rflag", "", &required_flag, Command::kPath);
-  Maybe<std::string> optional_flag;
+  std::optional<std::string> optional_flag;
   command.AddOptionalFlag("--oflag", "", &optional_flag, Command::kPath);
   std::vector<std::string> required_flag_list;
   command.AddRequiredFlagList("--rlflag", "", &required_flag_list, Command::kPath);
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index cd5015e..fe56018 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -47,7 +47,6 @@
 #include "io/ZipArchive.h"
 #include "trace/TraceBuffer.h"
 #include "util/Files.h"
-#include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlDom.h"
 #include "xml/XmlPullParser.h"
@@ -75,10 +74,10 @@
 };
 
 // Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
-                                                       const char dir_sep,
-                                                       std::string* out_error,
-                                                       const CompileOptions& options) {
+static std::optional<ResourcePathData> ExtractResourcePathData(const std::string& path,
+                                                               const char dir_sep,
+                                                               std::string* out_error,
+                                                               const CompileOptions& options) {
   std::vector<std::string> parts = util::Split(path, dir_sep);
   if (parts.size() < 2) {
     if (out_error) *out_error = "bad resource path";
@@ -337,7 +336,7 @@
     if (file_type == file::FileType::kDirectory) {
       context->GetDiagnostics()->Error(DiagMessage(input_path)
                                        << "resource file cannot be a directory");
-    } else if (file_type == file::FileType::kNonexistant) {
+    } else if (file_type == file::FileType::kNonExistant) {
       context->GetDiagnostics()->Error(DiagMessage(input_path) << "file not found");
     } else {
       context->GetDiagnostics()->Error(DiagMessage(input_path)
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1bc1f66..bd2e3d7 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -17,7 +17,10 @@
 #ifndef AAPT2_COMPILE_H
 #define AAPT2_COMPILE_H
 
-#include "androidfw/StringPiece.h"
+#include <optional>
+
+#include <androidfw/StringPiece.h>
+
 #include "format/Archive.h"
 #include "process/IResourceTableConsumer.h"
 #include "Command.h"
@@ -28,11 +31,11 @@
 
 struct CompileOptions {
   std::string output_path;
-  Maybe<std::string> source_path;
-  Maybe<std::string> res_dir;
-  Maybe<std::string> res_zip;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<Visibility::Level> visibility;
+  std::optional<std::string> source_path;
+  std::optional<std::string> res_dir;
+  std::optional<std::string> res_zip;
+  std::optional<std::string> generate_text_symbols_path;
+  std::optional<Visibility::Level> visibility;
   bool pseudolocalize = false;
   bool no_png_crunch = false;
   bool legacy_mode = false;
@@ -80,8 +83,8 @@
  private:
   IDiagnostics* diagnostic_;
   CompileOptions options_;
-  Maybe<std::string> visibility_;
-  Maybe<std::string> trace_folder_;
+  std::optional<std::string> visibility_;
+  std::optional<std::string> trace_folder_;
 };
 
 int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 22bcd85..3b097e0 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -367,8 +367,7 @@
     return 1;
   }
 
-  Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(),
-                                                             context.GetDiagnostics());
+  auto app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
   if (!app_info) {
     return 1;
   }
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 7e2029d..2cdb0c8 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -17,6 +17,8 @@
 #ifndef AAPT2_CONVERT_H
 #define AAPT2_CONVERT_H
 
+#include <optional>
+
 #include "Command.h"
 #include "LoadedApk.h"
 #include "format/binary/TableFlattener.h"
@@ -52,7 +54,7 @@
   TableFlattenerOptions table_flattener_options_;
   XmlFlattenerOptions xml_flattener_options_;
   std::string output_path_;
-  Maybe<std::string> output_format_;
+  std::optional<std::string> output_format_;
   bool verbose_ = false;
 };
 
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 3950f33..d9e8c92 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -87,8 +87,8 @@
 }
 
 template <typename Id>
-static bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
-                     const Visibility::Level& level_b, const Maybe<Id>& id_b) {
+static bool IsIdDiff(const Visibility::Level& level_a, const std::optional<Id>& id_a,
+                     const Visibility::Level& level_b, const std::optional<Id>& id_b) {
   if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
     return id_a != id_b;
   }
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3982d12..0a1e021 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -257,12 +257,11 @@
 }
 
 int DumpPackageNameCommand::Dump(LoadedApk* apk) {
-  Maybe<std::string> package_name = GetPackageName(apk);
-  if (!package_name) {
+  auto package_name = GetPackageName(apk);
+  if (!package_name.has_value()) {
     return 1;
   }
-
-  GetPrinter()->Println(package_name.value());
+  GetPrinter()->Println(*package_name);
   return 0;
 }
 
@@ -283,12 +282,12 @@
 }
 
 int DumpStyleParentCommand::Dump(LoadedApk* apk) {
-  Maybe<std::string> package_name = GetPackageName(apk);
-  if (!package_name) {
+  auto package_name = GetPackageName(apk);
+  if (!package_name.has_value()) {
     return 1;
   }
 
-  const auto target_style = ResourceName(package_name.value(), ResourceType::kStyle, style_);
+  const auto target_style = ResourceName(*package_name, ResourceType::kStyle, style_);
   const auto table = apk->GetResourceTable();
 
   if (!table) {
@@ -296,7 +295,7 @@
     return 1;
   }
 
-  Maybe<ResourceTable::SearchResult> target = table->FindResource(target_style);
+  std::optional<ResourceTable::SearchResult> target = table->FindResource(target_style);
   if (!target) {
     GetDiagnostics()->Error(
         DiagMessage() << "Target style \"" << target_style.entry << "\" does not exist");
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index cd51f7a..52616fa 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -43,17 +43,17 @@
     return diag_;
   }
 
-  Maybe<std::string> GetPackageName(LoadedApk* apk) {
+  std::optional<std::string> GetPackageName(LoadedApk* apk) {
     xml::Element* manifest_el = apk->GetManifest()->root.get();
     if (!manifest_el) {
       GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
-      return Maybe<std::string>();
+      return {};
     }
 
     xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
     if (!attr) {
       GetDiagnostics()->Error(DiagMessage() << "No package name.");
-      return Maybe<std::string>();
+      return {};
     }
     return attr->value;
   }
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e4d0f3b..e614a75 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -299,7 +299,7 @@
   bool do_not_fail_on_missing_resources = false;
   OutputFormat output_format = OutputFormat::kApk;
   std::unordered_set<std::string> extensions_to_not_compress;
-  Maybe<std::regex> regex_to_not_compress;
+  std::optional<std::regex> regex_to_not_compress;
 };
 
 // A sampling of public framework resource IDs.
@@ -741,7 +741,7 @@
     const size_t res_id_str_len = line.size() - res_id_start_idx;
     StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
 
-    Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
+    std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
     if (!maybe_id) {
       diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" << res_id_str
                                                      << "'");
@@ -793,7 +793,7 @@
     if (!options_.manifest_fixer_options.compile_sdk_version) {
       xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
       if (attr != nullptr) {
-        Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+        auto& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
         if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
           switch (prim->value.dataType) {
             case Res_value::TYPE_INT_DEC:
@@ -816,7 +816,7 @@
     if (!options_.manifest_fixer_options.compile_sdk_version_codename) {
       xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
       if (attr != nullptr) {
-        Maybe<std::string>& compile_sdk_version_codename =
+        std::optional<std::string>& compile_sdk_version_codename =
             options_.manifest_fixer_options.compile_sdk_version_codename;
         if (String* str = ValueCast<String>(attr->compiled_value.get())) {
           compile_sdk_version_codename = *str->value;
@@ -912,7 +912,7 @@
     return true;
   }
 
-  Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
+  std::optional<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
     TRACE_CALL();
     // Make sure the first element is <manifest> with package attribute.
     xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
@@ -937,7 +937,7 @@
 
     if (xml::Attribute* version_code_attr =
             manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
-      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
+      std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
       if (!maybe_code) {
         diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
                     << "invalid android:versionCode '" << version_code_attr->value << "'");
@@ -948,7 +948,7 @@
 
     if (xml::Attribute* version_code_major_attr =
         manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
-      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
+      std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
       if (!maybe_code) {
         diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
                         << "invalid android:versionCodeMajor '"
@@ -960,7 +960,7 @@
 
     if (xml::Attribute* revision_code_attr =
             manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
-      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
+      std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
       if (!maybe_code) {
         diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
                     << "invalid android:revisionCode '" << revision_code_attr->value << "'");
@@ -1094,7 +1094,7 @@
 
   bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
                      const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
-                     const Maybe<std::string>& out_text_symbols_path = {}) {
+                     const std::optional<std::string>& out_text_symbols_path = {}) {
     if (!options_.generate_java_class_path && !out_text_symbols_path) {
       return true;
     }
@@ -1251,7 +1251,7 @@
     }
 
     const std::string package_utf8 =
-        options_.custom_java_package.value_or_default(context_->GetCompilationPackage());
+        options_.custom_java_package.value_or(context_->GetCompilationPackage());
 
     std::string out_path = options_.generate_java_class_path.value();
     file::AppendPath(&out_path, file::PackageToPath(package_utf8));
@@ -1283,7 +1283,7 @@
     return true;
   }
 
-  bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) {
+  bool WriteProguardFile(const std::optional<std::string>& out, const proguard::KeepSet& keep_set) {
     TRACE_CALL();
     if (!out) {
       return true;
@@ -1374,7 +1374,7 @@
         res_name.package = context_->GetCompilationPackage();
       }
 
-      Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
+      std::optional<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
       if (mangled_name) {
         res_name = mangled_name.value();
       }
@@ -1550,7 +1550,7 @@
   bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
     std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
     for (const std::string& assets_dir : options_.assets_dirs) {
-      Maybe<std::vector<std::string>> files =
+      std::optional<std::vector<std::string>> files =
           file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
       if (!files) {
         return false;
@@ -1783,7 +1783,7 @@
         package_to_rewrite = table->packages.back().get();
         std::string new_package_name =
             StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
-                         app_info_.split_name.value_or_default("feature").c_str());
+                         app_info_.split_name.value_or("feature").c_str());
 
         if (context_->IsVerbose()) {
           context_->GetDiagnostics()->Note(
@@ -1823,7 +1823,7 @@
     }
 
     // First extract the Package name without modifying it (via --rename-manifest-package).
-    if (Maybe<AppInfo> maybe_app_info =
+    if (std::optional<AppInfo> maybe_app_info =
             ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
       const AppInfo& app_info = maybe_app_info.value();
       context_->SetCompilationPackage(app_info.package);
@@ -1850,14 +1850,14 @@
       return 1;
     }
 
-    Maybe<AppInfo> maybe_app_info =
+    std::optional<AppInfo> maybe_app_info =
         ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
     if (!maybe_app_info) {
       return 1;
     }
 
     app_info_ = maybe_app_info.value();
-    context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
+    context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or(0));
 
     context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
     context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
@@ -2231,7 +2231,7 @@
   std::map<size_t, std::string> shared_libs_;
 
   // The package name of the base application, if it is included.
-  Maybe<std::string> included_feature_base_;
+  std::optional<std::string> included_feature_base_;
 };
 
 int LinkCommand::Action(const std::vector<std::string>& args) {
@@ -2315,7 +2315,8 @@
       return 1;
     }
 
-    const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
+    const std::optional<uint32_t> maybe_package_id_int =
+        ResourceUtils::ParseInt(package_id_.value());
     if (!maybe_package_id_int) {
       context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
                                                     << "' is not a valid integer");
@@ -2360,7 +2361,7 @@
   }
 
   if (preferred_density_) {
-    Maybe<uint16_t> density =
+    std::optional<uint16_t> density =
         ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
     if (!density) {
       return 1;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 768b4b2..d8c76e2 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -45,21 +45,21 @@
   bool auto_add_overlay = false;
   bool override_styles_instead_of_overlaying = false;
   OutputFormat output_format = OutputFormat::kApk;
-  Maybe<std::string> rename_resources_package;
+  std::optional<std::string> rename_resources_package;
 
   // Java/Proguard options.
-  Maybe<std::string> generate_java_class_path;
-  Maybe<std::string> custom_java_package;
+  std::optional<std::string> generate_java_class_path;
+  std::optional<std::string> custom_java_package;
   std::set<std::string> extra_java_packages;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<std::string> generate_proguard_rules_path;
-  Maybe<std::string> generate_main_dex_proguard_rules_path;
+  std::optional<std::string> generate_text_symbols_path;
+  std::optional<std::string> generate_proguard_rules_path;
+  std::optional<std::string> generate_main_dex_proguard_rules_path;
   bool generate_conditional_proguard_rules = false;
   bool generate_minimal_proguard_rules = false;
   bool generate_non_final_ids = false;
   bool no_proguard_location_reference = false;
   std::vector<std::string> javadoc_annotations;
-  Maybe<std::string> private_symbols;
+  std::optional<std::string> private_symbols;
 
   // Optimizations/features.
   bool no_auto_version = false;
@@ -70,7 +70,7 @@
   bool no_xml_namespaces = false;
   bool do_not_compress_anything = false;
   std::unordered_set<std::string> extensions_to_not_compress;
-  Maybe<std::regex> regex_to_not_compress;
+  std::optional<std::regex> regex_to_not_compress;
 
   // Static lib options.
   bool no_static_lib_packages = false;
@@ -97,7 +97,7 @@
 
   // Stable ID options.
   std::unordered_map<ResourceName, ResourceId> stable_id_map;
-  Maybe<std::string> resource_id_map_path;
+  std::optional<std::string> resource_id_map_path;
 
   // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
   // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
@@ -321,20 +321,20 @@
 
   std::vector<std::string> overlay_arg_list_;
   std::vector<std::string> extra_java_packages_;
-  Maybe<std::string> package_id_;
+  std::optional<std::string> package_id_;
   std::vector<std::string> configs_;
-  Maybe<std::string> preferred_density_;
-  Maybe<std::string> product_list_;
-  Maybe<std::string> no_compress_regex;
+  std::optional<std::string> preferred_density_;
+  std::optional<std::string> product_list_;
+  std::optional<std::string> no_compress_regex;
   bool legacy_x_flag_ = false;
   bool require_localization_ = false;
   bool verbose_ = false;
   bool shared_lib_ = false;
   bool static_lib_ = false;
   bool proto_format_ = false;
-  Maybe<std::string> stable_id_file_path_;
+  std::optional<std::string> stable_id_file_path_;
   std::vector<std::string> split_args_;
-  Maybe<std::string> trace_folder_;
+  std::optional<std::string> trace_folder_;
 };
 
 }// namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 5b18a37..caa3e60 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -354,7 +354,7 @@
     return false;
   }
 
-  Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
+  auto app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
   if (!app_info) {
     context->GetDiagnostics()->Error(DiagMessage()
                                      << "failed to extract data from AndroidManifest.xml");
@@ -362,7 +362,7 @@
   }
 
   out_options->app_info = std::move(app_info.value());
-  context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0));
+  context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or(0));
   return true;
 }
 
@@ -380,7 +380,7 @@
 
   if (config_path_) {
     std::string& path = config_path_.value();
-    Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
+    std::optional<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
     if (for_path) {
       options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
       if (!options_.apk_artifacts) {
@@ -427,7 +427,7 @@
   if (target_densities_) {
     // Parse the target screen densities.
     for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
-      Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
+      std::optional<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
       if (!target_density) {
         return 1;
       }
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 3afc46b..ff63e8d 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -29,9 +29,9 @@
   friend class OptimizeCommand;
 
   // Path to the output APK.
-  Maybe<std::string> output_path;
+  std::optional<std::string> output_path;
   // Path to the output APK directory for splits.
-  Maybe<std::string> output_dir;
+  std::optional<std::string> output_dir;
 
   // Details of the app extracted from the AndroidManifest.xml
   AppInfo app_info;
@@ -50,7 +50,7 @@
 
   TableFlattenerOptions table_flattener_options;
 
-  Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+  std::optional<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
 
   // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
   // are kept and will be written as output.
@@ -60,7 +60,7 @@
   bool shorten_resource_paths = false;
 
   // Path to the output map of original resource paths to shortened paths.
-  Maybe<std::string> shortened_paths_map_path;
+  std::optional<std::string> shortened_paths_map_path;
 };
 
 class OptimizeCommand : public Command {
@@ -122,9 +122,9 @@
   bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map,
                                const std::string &file_path);
 
-  Maybe<std::string> config_path_;
-  Maybe<std::string> resources_config_path_;
-  Maybe<std::string> target_densities_;
+  std::optional<std::string> config_path_;
+  std::optional<std::string> resources_config_path_;
+  std::optional<std::string> target_densities_;
   std::vector<std::string> configs_;
   std::vector<std::string> split_args_;
   std::unordered_set<std::string> kept_artifacts_;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 7214f1a..3244fb8 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -21,11 +21,10 @@
 #include "android-base/logging.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/Locale.h"
-
 #include "ResourceUtils.h"
 #include "ValueVisitor.h"
 #include "split/TableSplitter.h"
-#include "util/Maybe.h"
+
 #include "util/Util.h"
 
 using ::android::ConfigDescription;
@@ -35,7 +34,7 @@
 
 namespace aapt {
 
-Maybe<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
+std::optional<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
   ConfigDescription preferred_density_config;
   if (!ConfigDescription::Parse(arg, &preferred_density_config)) {
     diag->Error(DiagMessage() << "invalid density '" << arg << "' for --preferred-density option");
@@ -245,8 +244,8 @@
   return doc;
 }
 
-static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
-                                                std::string* out_error) {
+static std::optional<std::string> ExtractCompiledString(const xml::Attribute& attr,
+                                                        std::string* out_error) {
   if (attr.compiled_value != nullptr) {
     const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
     if (compiled_str != nullptr) {
@@ -269,7 +268,8 @@
   return {};
 }
 
-static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<uint32_t> ExtractCompiledInt(const xml::Attribute& attr,
+                                                  std::string* out_error) {
   if (attr.compiled_value != nullptr) {
     const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
     if (compiled_prim != nullptr) {
@@ -283,7 +283,7 @@
   }
 
   // Fallback to the plain text value if there is one.
-  Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
+  std::optional<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
   if (integer) {
     return integer;
   }
@@ -293,7 +293,7 @@
   return {};
 }
 
-static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
   if (attr.compiled_value != nullptr) {
     const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
     if (compiled_prim != nullptr) {
@@ -307,7 +307,7 @@
 
     const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
     if (compiled_str != nullptr) {
-      Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
+      std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
       if (sdk_version) {
         return sdk_version;
       }
@@ -320,7 +320,7 @@
   }
 
   // Fallback to the plain text value if there is one.
-  Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
+  std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
   if (sdk_version) {
     return sdk_version;
   }
@@ -330,8 +330,8 @@
   return {};
 }
 
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
-                                                IDiagnostics* diag) {
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+                                                        IDiagnostics* diag) {
   // Make sure the first element is <manifest> with package attribute.
   const xml::Element* manifest_el = xml_res.root.get();
   if (manifest_el == nullptr) {
@@ -352,7 +352,7 @@
   }
 
   std::string error_msg;
-  Maybe<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
+  std::optional<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
   if (!maybe_package) {
     diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
                 << "invalid package name: " << error_msg);
@@ -362,7 +362,7 @@
 
   if (const xml::Attribute* version_code_attr =
           manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
-    Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
+    std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
     if (!maybe_code) {
       diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
                   << "invalid android:versionCode: " << error_msg);
@@ -373,7 +373,7 @@
 
   if (const xml::Attribute* version_code_major_attr =
       manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
-    Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
+    std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
     if (!maybe_code) {
       diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
                       << "invalid android:versionCodeMajor: " << error_msg);
@@ -384,7 +384,7 @@
 
   if (const xml::Attribute* revision_code_attr =
           manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
-    Maybe<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
+    std::optional<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
     if (!maybe_code) {
       diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
                   << "invalid android:revisionCode: " << error_msg);
@@ -394,7 +394,8 @@
   }
 
   if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
-    Maybe<std::string> maybe_split_name = ExtractCompiledString(*split_name_attr, &error_msg);
+    std::optional<std::string> maybe_split_name =
+        ExtractCompiledString(*split_name_attr, &error_msg);
     if (!maybe_split_name) {
       diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
                   << "invalid split name: " << error_msg);
@@ -406,7 +407,7 @@
   if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
     if (const xml::Attribute* min_sdk =
             uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
-      Maybe<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
+      std::optional<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
       if (!maybe_sdk) {
         diag->Error(DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number))
                     << "invalid android:minSdkVersion: " << error_msg);
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 2a7c62e..1b98eb4 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -26,14 +26,14 @@
 #include "SdkConstants.h"
 #include "filter/ConfigFilter.h"
 #include "split/TableSplitter.h"
-#include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
 namespace aapt {
 
 // Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
 // Returns Nothing and logs a human friendly error message if the string was not legal.
-Maybe<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg, IDiagnostics* diag);
+std::optional<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg,
+                                                    IDiagnostics* diag);
 
 // Parses a string of the form 'path/to/output.apk:<config>[,<config>...]' and fills in
 // `out_path` with the path and `out_split` with the set of ConfigDescriptions.
@@ -59,8 +59,8 @@
                                                         const SplitConstraints& constraints);
 
 // Extracts relevant info from the AndroidManifest.xml.
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
-                                                IDiagnostics* diag);
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+                                                        IDiagnostics* diag);
 
 // Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
 // replacing nonconforming characters with underscores.
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 6637766..d357571 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -57,18 +57,18 @@
   ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
   ASSERT_TRUE(VerifyIds(table.get()));
 
-  Maybe<ResourceTable::SearchResult> maybe_result;
+  std::optional<ResourceTable::SearchResult> maybe_result;
 
   // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
 
   maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
   ASSERT_TRUE(maybe_result);
-  EXPECT_EQ(make_value<ResourceId>(0x01020000), maybe_result.value().entry->id);
+  EXPECT_EQ(0x01020000, maybe_result.value().entry->id);
 
   maybe_result =
       table->FindResource(test::ParseNameOrDie("android:integer/three"));
   ASSERT_TRUE(maybe_result);
-  EXPECT_EQ(make_value<ResourceId>(0x01030000), maybe_result.value().entry->id);
+  EXPECT_EQ(0x01030000, maybe_result.value().entry->id);
 
   // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
   // IDs.
@@ -76,17 +76,17 @@
   maybe_result =
       table->FindResource(test::ParseNameOrDie("android:string/five"));
   ASSERT_TRUE(maybe_result);
-  EXPECT_EQ(make_value<ResourceId>(0x01050000), maybe_result.value().entry->id);
+  EXPECT_EQ(0x01050000, maybe_result.value().entry->id);
 
   // Expect to fill in the gaps between 0x01040000 and 0x01040006.
 
   maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
   ASSERT_TRUE(maybe_result);
-  EXPECT_EQ(make_value<ResourceId>(0x01040001), maybe_result.value().entry->id);
+  EXPECT_EQ(0x01040001, maybe_result.value().entry->id);
 
   maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
   ASSERT_TRUE(maybe_result);
-  EXPECT_EQ(make_value<ResourceId>(0x01040002), maybe_result.value().entry->id);
+  EXPECT_EQ(0x01040002, maybe_result.value().entry->id);
 }
 
 TEST_F(IdAssignerTests, FailWhenNonUniqueIdsAssigned) {
@@ -143,7 +143,7 @@
   ASSERT_TRUE(result);
 
   const ResourceTable::SearchResult& search_result = result.value();
-  EXPECT_EQ(make_value<ResourceId>(0x01010002), search_result.entry->id);
+  EXPECT_EQ(0x01010002, search_result.entry->id);
 }
 
 TEST_F(IdAssignerTests, UseAllEntryIds) {
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 79b0933..de1c3bb 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -56,7 +56,7 @@
       return;
     }
 
-    Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
+    std::optional<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
     if (!ref) {
       context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid XML attribute '" << attr->value
                                                          << "'");
@@ -65,7 +65,7 @@
     }
 
     const ResourceName& name = ref.value().name.value();
-    Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
+    std::optional<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
     if (!maybe_pkg) {
       context_->GetDiagnostics()->Error(DiagMessage(src)
                                         << "invalid namespace prefix '" << name.package << "'");
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 3f574ee..2461438 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -33,7 +33,7 @@
 // The struct that represents both Span objects and UntranslatableSections.
 struct UnifiedSpan {
   // Only present for Span objects. If not present, this was an UntranslatableSection.
-  Maybe<std::string> tag;
+  std::optional<std::string> tag;
 
   // The UTF-16 index into the string where this span starts.
   uint32_t first_char;
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index dd06b38..e7a4585 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -34,7 +34,6 @@
 #include "io/FileSystem.h"
 #include "io/StringStream.h"
 #include "util/Files.h"
-#include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
@@ -113,7 +112,7 @@
 }
 
 /** Returns the value of the version-code-order attribute for a given element. */
-Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+std::optional<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
   const xml::Attribute* version = element->FindAttribute("", "version-code-order");
   if (version == nullptr) {
     std::string label = GetLabel(element, diag);
@@ -135,7 +134,7 @@
 
 /** Copies the values referenced in a configuration group to the target list. */
 template <typename T>
-bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
+bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
                        std::vector<T>* target) {
   // If there was no item configured, there is nothing to do and no error.
   if (!name) {
@@ -159,7 +158,7 @@
  * success, or false if the either the placeholder is not found in the name, or the value is not
  * present and the placeholder was.
  */
-bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
+bool ReplacePlaceholder(const StringPiece& placeholder, const std::optional<StringPiece>& value,
                         std::string* name, IDiagnostics* diag) {
   size_t offset = name->find(placeholder.data());
   bool found = (offset != std::string::npos);
@@ -207,17 +206,17 @@
 }
 
 /** Converts a ConfiguredArtifact into an OutputArtifact. */
-Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
-                                       const std::string& apk_name,
-                                       const PostProcessingConfiguration& config,
-                                       IDiagnostics* diag) {
+std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
+                                               const std::string& apk_name,
+                                               const PostProcessingConfiguration& config,
+                                               IDiagnostics* diag) {
   if (!artifact.name && !config.artifact_format) {
     diag->Error(
         DiagMessage() << "Artifact does not have a name and no global name template defined");
     return {};
   }
 
-  Maybe<std::string> artifact_name =
+  std::optional<std::string> artifact_name =
       (artifact.name) ? artifact.Name(apk_name, diag)
                       : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
 
@@ -287,9 +286,9 @@
 namespace configuration {
 
 /** Returns the binary reprasentation of the XML configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
-                                                        const std::string& config_path,
-                                                        IDiagnostics* diag) {
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+                                                                const std::string& config_path,
+                                                                IDiagnostics* diag) {
   StringInputStream in(contents);
   std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
   if (!doc) {
@@ -351,7 +350,8 @@
 /**
  * Returns the common artifact base name from a template string.
  */
-Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+std::optional<std::string> ToBaseName(std::string result, const StringPiece& apk_name,
+                                      IDiagnostics* diag) {
   const StringPiece ext = file::GetExtension(apk_name);
   size_t end_index = apk_name.to_string().rfind(ext.to_string());
   const std::string base_name =
@@ -359,8 +359,8 @@
 
   // Base name is optional.
   if (result.find("${basename}") != std::string::npos) {
-    Maybe<StringPiece> maybe_base_name =
-        base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
+    auto maybe_base_name = base_name.empty() ? std::nullopt
+                                             : std::optional<StringPiece>{base_name};
     if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
       return {};
     }
@@ -383,10 +383,10 @@
   return result;
 }
 
-Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
-                                                      const StringPiece& apk_name,
-                                                      IDiagnostics* diag) const {
-  Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+std::optional<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
+                                                              const StringPiece& apk_name,
+                                                              IDiagnostics* diag) const {
+  std::optional<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
   if (!base) {
     return {};
   }
@@ -419,7 +419,8 @@
   return result;
 }
 
-Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
+std::optional<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name,
+                                                    IDiagnostics* diag) const {
   if (!name) {
     return {};
   }
@@ -430,7 +431,7 @@
 }  // namespace configuration
 
 /** Returns a ConfigurationParser for the file located at the provided path. */
-Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
+std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
   std::string contents;
   if (!ReadFileToString(path, &contents, true)) {
     return {};
@@ -442,9 +443,9 @@
     : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
 }
 
-Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
+std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
     const android::StringPiece& apk_path) {
-  Maybe<PostProcessingConfiguration> maybe_config =
+  std::optional<PostProcessingConfiguration> maybe_config =
       ExtractConfiguration(contents_, config_path_, diag_);
   if (!maybe_config) {
     return {};
@@ -460,7 +461,8 @@
   int version = 1;
 
   for (const ConfiguredArtifact& artifact : config.artifacts) {
-    Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
+    std::optional<OutputArtifact> output_artifact =
+        ToOutputArtifact(artifact, apk_name, config, diag_);
     if (!output_artifact) {
       // Defer return an error condition so that all errors are reported.
       valid = false;
@@ -538,7 +540,7 @@
 
   bool valid = true;
   OrderedEntry<Abi>& entry = config->abi_groups[label];
-  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
   if (!order) {
     valid = false;
   } else {
@@ -589,7 +591,7 @@
 
   bool valid = true;
   OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
-  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
   if (!order) {
     valid = false;
   } else {
@@ -656,7 +658,7 @@
 
   bool valid = true;
   OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
-  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
   if (!order) {
     valid = false;
   } else {
@@ -724,19 +726,19 @@
       entry.label = attr.value;
       valid_attr = true;
     } else if (attr.name == "minSdkVersion") {
-      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+      std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
       if (version) {
         valid_attr = true;
         entry.min_sdk_version = version.value();
       }
     } else if (attr.name == "targetSdkVersion") {
-      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+      std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
       if (version) {
         valid_attr = true;
         entry.target_sdk_version = version;
       }
     } else if (attr.name == "maxSdkVersion") {
-      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+      std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
       if (version) {
         valid_attr = true;
         entry.max_sdk_version = version;
@@ -778,7 +780,7 @@
 
   bool valid = true;
   OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
-  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
   if (!order) {
     valid = false;
   } else {
@@ -828,7 +830,7 @@
 
   bool valid = true;
   OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
-  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
   if (!order) {
     valid = false;
   } else {
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index b9e3be9..195b4ba 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -17,6 +17,7 @@
 #ifndef AAPT2_CONFIGURATION_H
 #define AAPT2_CONFIGURATION_H
 
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -25,7 +26,6 @@
 #include "androidfw/ConfigDescription.h"
 
 #include "Diagnostics.h"
-#include "util/Maybe.h"
 
 namespace aapt {
 
@@ -55,9 +55,9 @@
  */
 struct Locale {
   /** The ISO<?> standard locale language code. */
-  Maybe<std::string> lang;
+  std::optional<std::string> lang;
   /** The ISO<?> standard locale region code. */
-  Maybe<std::string> region;
+  std::optional<std::string> region;
 
   inline friend bool operator==(const Locale& lhs, const Locale& rhs) {
     return lhs.lang == rhs.lang && lhs.region == rhs.region;
@@ -74,9 +74,9 @@
 struct AndroidSdk {
   std::string label;
   int min_sdk_version;  // min_sdk_version is mandatory if splitting by SDK.
-  Maybe<int> target_sdk_version;
-  Maybe<int> max_sdk_version;
-  Maybe<AndroidManifest> manifest;
+  std::optional<int> target_sdk_version;
+  std::optional<int> max_sdk_version;
+  std::optional<AndroidManifest> manifest;
 
   static AndroidSdk ForMinSdk(int min_sdk) {
     AndroidSdk sdk;
@@ -112,7 +112,7 @@
   std::vector<Abi> abis;
   std::vector<android::ConfigDescription> screen_densities;
   std::vector<android::ConfigDescription> locales;
-  Maybe<AndroidSdk> android_sdk;
+  std::optional<AndroidSdk> android_sdk;
   std::vector<DeviceFeature> features;
   std::vector<GlTexture> textures;
 
@@ -136,7 +136,7 @@
  public:
 
   /** Returns a ConfigurationParser for the file located at the provided path. */
-  static Maybe<ConfigurationParser> ForPath(const std::string& path);
+  static std::optional<ConfigurationParser> ForPath(const std::string& path);
 
   /** Returns a ConfigurationParser for the configuration in the provided file contents. */
   static ConfigurationParser ForContents(const std::string& contents, const std::string& path) {
@@ -154,7 +154,8 @@
    * Parses the configuration file and returns the results. If the configuration could not be parsed
    * the result is empty and any errors will be displayed with the provided diagnostics context.
    */
-  Maybe<std::vector<configuration::OutputArtifact>> Parse(const android::StringPiece& apk_path);
+  std::optional<std::vector<configuration::OutputArtifact>> Parse(
+      const android::StringPiece& apk_path);
 
  protected:
   /**
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index c541688..42ef5159 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -84,8 +84,8 @@
    * have not been able to determine the sort order with the previous comparisons.
    */
   template <typename T>
-  ComparisonChain& Add(const Group<T>& groups, const Maybe<std::string>& lhs,
-                       const Maybe<std::string>& rhs) {
+  ComparisonChain& Add(const Group<T>& groups, const std::optional<std::string>& lhs,
+                       const std::optional<std::string>& rhs) {
     return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
   }
 
@@ -108,7 +108,7 @@
 
  private:
   template <typename T>
-  inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
+  inline size_t GetGroupOrder(const Entry<T>& groups, const std::optional<std::string>& label) {
     if (!label) {
       return std::numeric_limits<size_t>::max();
     }
@@ -122,32 +122,33 @@
 /** Output artifact configuration options. */
 struct ConfiguredArtifact {
   /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
-  Maybe<std::string> name;
+  std::optional<std::string> name;
   /** If present, uses the ABI group with this name. */
-  Maybe<std::string> abi_group;
+  std::optional<std::string> abi_group;
   /** If present, uses the screen density group with this name. */
-  Maybe<std::string> screen_density_group;
+  std::optional<std::string> screen_density_group;
   /** If present, uses the locale group with this name. */
-  Maybe<std::string> locale_group;
+  std::optional<std::string> locale_group;
   /** If present, uses the Android SDK with this name. */
-  Maybe<std::string> android_sdk;
+  std::optional<std::string> android_sdk;
   /** If present, uses the device feature group with this name. */
-  Maybe<std::string> device_feature_group;
+  std::optional<std::string> device_feature_group;
   /** If present, uses the OpenGL texture group with this name. */
-  Maybe<std::string> gl_texture_group;
+  std::optional<std::string> gl_texture_group;
 
   /** Convert an artifact name template into a name string based on configuration contents. */
-  Maybe<std::string> ToArtifactName(const android::StringPiece& format,
-                                    const android::StringPiece& apk_name, IDiagnostics* diag) const;
+  std::optional<std::string> ToArtifactName(const android::StringPiece& format,
+                                            const android::StringPiece& apk_name,
+                                            IDiagnostics* diag) const;
 
   /** Convert an artifact name template into a name string based on configuration contents. */
-  Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+  std::optional<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
 };
 
 /** AAPT2 XML configuration file binary representation. */
 struct PostProcessingConfiguration {
   std::vector<ConfiguredArtifact> artifacts;
-  Maybe<std::string> artifact_format;
+  std::optional<std::string> artifact_format;
 
   Group<Abi> abi_groups;
   Group<android::ConfigDescription> screen_density_groups;
@@ -212,9 +213,9 @@
 };
 
 /** Parses the provided XML document returning the post processing configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
-                                                        const std::string& config_path,
-                                                        IDiagnostics* diag);
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+                                                                const std::string& config_path,
+                                                                IDiagnostics* diag);
 
 namespace handler {
 
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index e5b3107..e5eaccc 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -31,11 +31,6 @@
 namespace aapt {
 
 namespace configuration {
-void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
-  *os << "SDK: min=" << sdk.min_sdk_version
-      << ", target=" << sdk.target_sdk_version.value_or_default(-1)
-      << ", max=" << sdk.max_sdk_version.value_or_default(-1);
-}
 
 bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
   return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group &&
@@ -45,20 +40,6 @@
          lhs.gl_texture_group == rhs.gl_texture_group;
 }
 
-std::ostream& operator<<(std::ostream& out, const Maybe<std::string>& value) {
-  PrintTo(value, &out);
-  return out;
-}
-
-void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) {
-  *os << "\n{"
-      << "\n  name: " << artifact.name << "\n  sdk: " << artifact.android_sdk
-      << "\n  abi: " << artifact.abi_group << "\n  density: " << artifact.screen_density_group
-      << "\n  locale: " << artifact.locale_group
-      << "\n  features: " << artifact.device_feature_group
-      << "\n  textures: " << artifact.gl_texture_group << "\n}\n";
-}
-
 namespace handler {
 
 namespace {
@@ -186,7 +167,7 @@
 }
 
 TEST_F(ConfigurationParserTest, ExtractConfiguration) {
-  Maybe<PostProcessingConfiguration> maybe_config =
+  std::optional<PostProcessingConfiguration> maybe_config =
       ExtractConfiguration(kValidConfig, "fake.xml", &diag_);
 
   PostProcessingConfiguration config = maybe_config.value();
@@ -928,7 +909,8 @@
 
   EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
 
-  const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
+  const std::optional<std::string>& name =
+      x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
   ASSERT_TRUE(name);
   EXPECT_EQ(name.value(), "something.${abix86}.apk");
 }
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
index 41f01a0..c20b053 100644
--- a/tools/aapt2/format/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -43,7 +43,7 @@
   bool Open(const StringPiece& out_dir) {
     dir_ = out_dir.to_string();
     file::FileType type = file::GetFileType(dir_);
-    if (type == file::FileType::kNonexistant) {
+    if (type == file::FileType::kNonExistant) {
       error_ = "directory does not exist";
       return false;
     } else if (type != file::FileType::kDirectory) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 8139d73..cd1c0af 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -123,7 +123,7 @@
       return ::testing::AssertionFailure() << "failed to find resource name";
     }
 
-    Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
+    std::optional<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
     if (!resName) {
       return ::testing::AssertionFailure()
              << "expected name '" << expected_res_name << "' but got '"
@@ -423,7 +423,7 @@
   ResourceTable result;
   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
 
-  Maybe<ResourceTable::SearchResult> search_result =
+  std::optional<ResourceTable::SearchResult> search_result =
       result.FindResource(test::ParseNameOrDie("lib:id/foo"));
   ASSERT_TRUE(search_result);
   EXPECT_EQ(0x00u, search_result.value().entry->id.value().package_id());
@@ -454,7 +454,7 @@
   ResourceTable result;
   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
 
-  Maybe<ResourceTable::SearchResult> search_result =
+  std::optional<ResourceTable::SearchResult> search_result =
       result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
   ASSERT_TRUE(search_result);
   EXPECT_EQ(0x00030001u, search_result.value().entry->id.value());
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index afbaae4..cdbe882 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -264,7 +264,7 @@
       }
 
       std::string processed_str;
-      Maybe<StringPiece> compiled_text;
+      std::optional<StringPiece> compiled_text;
       if (xml_attr->compiled_value != nullptr) {
         // Make sure we're not flattening a String. A String can be referencing a string from
         // a different StringPool than we're using here to build the binary XML.
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 6042ba8..f3b7f75 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -438,7 +438,7 @@
 }
 
 static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
-  pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
+  pb_ref->set_id(ref.id.value_or(ResourceId(0x0)).id);
 
   if (ref.name) {
     pb_ref->set_name(ref.name.value().to_string());
@@ -759,13 +759,13 @@
     pb_attr->set_namespace_uri(attr.namespace_uri);
     pb_attr->set_value(attr.value);
     if (attr.compiled_attribute) {
-      const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
+      const ResourceId attr_id = attr.compiled_attribute.value().id.value_or(ResourceId{});
       pb_attr->set_resource_id(attr_id.id);
     }
     if (attr.compiled_value != nullptr) {
       SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
       pb::SourcePosition* pb_src = pb_attr->mutable_source();
-      pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
+      pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or(0));
     }
   }
 
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 38c811f..d1d72e0 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -189,7 +189,7 @@
   ASSERT_THAT(new_id, NotNull());
   EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
 
-  Maybe<ResourceTable::SearchResult> result =
+  std::optional<ResourceTable::SearchResult> result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
   ASSERT_TRUE(result);
 
@@ -234,7 +234,7 @@
   EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
   EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
 
-  Maybe<ResourceTable::SearchResult> search_result =
+  std::optional<ResourceTable::SearchResult> search_result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
@@ -637,7 +637,7 @@
   ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
   EXPECT_THAT(error, IsEmpty());
 
-  Maybe<ResourceTable::SearchResult> search_result =
+  std::optional<ResourceTable::SearchResult> search_result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index e15f935..fc2e45e 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -21,11 +21,10 @@
 #include "android-base/errors.h"
 #include "androidfw/StringPiece.h"
 #include "utils/FileMap.h"
-
 #include "Source.h"
 #include "io/FileStream.h"
 #include "util/Files.h"
-#include "util/Maybe.h"
+
 #include "util/Util.h"
 
 using ::android::StringPiece;
@@ -38,7 +37,7 @@
 
 std::unique_ptr<IData> RegularFile::OpenAsData() {
   android::FileMap map;
-  if (Maybe<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
+  if (std::optional<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
     if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
       return util::make_unique<MmappedData>(std::move(map.value()));
     }
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index de6524d..3b3c6e1 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -204,7 +204,7 @@
 }
 
 // Whether or not to skip writing this symbol.
-bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
+bool JavaClassGenerator::SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol) {
   return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
                      !symbol.value().is_public);
 }
@@ -212,12 +212,12 @@
 struct StyleableAttr {
   const Reference* attr_ref = nullptr;
   std::string field_name;
-  Maybe<SymbolTable::Symbol> symbol;
+  std::optional<SymbolTable::Symbol> symbol;
 };
 
 static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
-  const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
-  const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
+  const ResourceId lhs_id = lhs.attr_ref->id.value_or(ResourceId(0));
+  const ResourceId rhs_id = rhs.attr_ref->id.value_or(ResourceId(0));
   if (lhs_id == rhs_id) {
     return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
   }
@@ -362,7 +362,7 @@
       array_def->AddElement(field_name);
       r_txt_contents = field_name.ref;
     } else {
-      const ResourceId attr_id = attr.attr_ref->id.value_or_default(ResourceId(0));
+      const ResourceId attr_id = attr.attr_ref->id.value_or(ResourceId(0));
       array_def->AddElement(attr_id);
       r_txt_contents = to_string(attr_id);
     }
@@ -504,9 +504,9 @@
   }
 }
 
-Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
-                                                        const StringPiece& package_name_to_generate,
-                                                        const ResourceEntry& entry) {
+std::optional<std::string> JavaClassGenerator::UnmangleResource(
+    const StringPiece& package_name, const StringPiece& package_name_to_generate,
+    const ResourceEntry& entry) {
   if (SkipSymbol(entry.visibility.level)) {
     return {};
   }
@@ -535,7 +535,7 @@
                                      MethodDefinition* out_rewrite_method_def,
                                      Printer* r_txt_printer) {
   for (const auto& entry : type.entries) {
-    const Maybe<std::string> unmangled_name =
+    const std::optional<std::string> unmangled_name =
         UnmangleResource(package.name, package_name_to_generate, *entry);
     if (!unmangled_name) {
       continue;
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index d9d1b39..b45a2f1 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -46,7 +46,7 @@
 
   // If set, generates code to rewrite the package ID of resources.
   // Implies use_final == true. Default is unset.
-  Maybe<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
+  std::optional<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
 
   enum class SymbolTypes {
     kAll,
@@ -83,13 +83,13 @@
 
  private:
   bool SkipSymbol(Visibility::Level state);
-  bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
+  bool SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol);
 
   // Returns the unmangled resource entry name if the unmangled package is the same as
   // package_name_to_generate. Returns nothing if the resource should be skipped.
-  Maybe<std::string> UnmangleResource(const android::StringPiece& package_name,
-                                      const android::StringPiece& package_name_to_generate,
-                                      const ResourceEntry& entry);
+  std::optional<std::string> UnmangleResource(const android::StringPiece& package_name,
+                                              const android::StringPiece& package_name_to_generate,
+                                              const ResourceEntry& entry);
 
   bool ProcessType(const android::StringPiece& package_name_to_generate,
                    const ResourceTablePackage& package, const ResourceTableType& type,
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 09ea03b..a0db41b 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -19,19 +19,17 @@
 #include <algorithm>
 
 #include "Source.h"
-#include "java/AnnotationProcessor.h"
 #include "java/ClassDefinition.h"
 #include "java/JavaClassGenerator.h"
 #include "text/Unicode.h"
-#include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
 using ::aapt::text::IsJavaIdentifier;
 
 namespace aapt {
 
-static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
-                                                const std::string& value) {
+static std::optional<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+                                                        const std::string& value) {
   std::string result = value;
   size_t pos = value.rfind('.');
   if (pos != std::string::npos) {
@@ -63,7 +61,7 @@
     return false;
   }
 
-  Maybe<std::string> result =
+  std::optional<std::string> result =
       ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
   if (!result) {
     return false;
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d9a4caa..b939f35 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -48,7 +48,7 @@
 
   void Visit(xml::Element* node) override {
     if (!node->namespace_uri.empty()) {
-      Maybe<xml::ExtractedPackage> maybe_package =
+      std::optional<xml::ExtractedPackage> maybe_package =
           xml::ExtractPackageFromNamespace(node->namespace_uri);
       if (maybe_package) {
         // This is a custom view, let's figure out the class name from this.
@@ -270,14 +270,16 @@
         get_name = true;
         xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
         if (attr) {
-          Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+          std::optional<std::string> result =
+              util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
             AddClass(node->line_number, result.value(), "");
           }
         }
         attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
         if (attr) {
-          Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+          std::optional<std::string> result =
+              util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
             AddClass(node->line_number, result.value(), "");
           }
@@ -285,7 +287,8 @@
 
         attr = node->FindAttribute(xml::kSchemaAndroid, "zygotePreloadName");
         if (attr) {
-          Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+          std::optional<std::string> result =
+              util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
             AddClass(node->line_number, result.value(), "");
           }
@@ -317,7 +320,8 @@
         get_name = attr != nullptr;
 
         if (get_name) {
-          Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+          std::optional<std::string> result =
+              util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
             AddClass(node->line_number, result.value(), "");
           }
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 876494e..328ac97 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -90,7 +90,7 @@
           }
 
           if (Style* style = ValueCast<Style>(config_value->value.get())) {
-            Maybe<ApiVersion> min_sdk_stripped;
+            std::optional<ApiVersion> min_sdk_stripped;
             std::vector<Style::Entry> stripped;
 
             auto iter = style->entries.begin();
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 02fd00b..8179d46 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -87,25 +87,27 @@
   Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v4"));
   ASSERT_THAT(style, NotNull());
   ASSERT_EQ(style->entries.size(), 1u);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries.front().key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries.front().key.name);
 
   style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v13"));
   ASSERT_THAT(style, NotNull());
   ASSERT_EQ(style->entries.size(), 2u);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")),style->entries[0].key.name);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+            style->entries[1].key.name);
 
   style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v17"));
   ASSERT_THAT(style, NotNull());
   ASSERT_EQ(style->entries.size(), 3u);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries[0].key.name);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingStart")), style->entries[2].key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+            style->entries[1].key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingStart"), style->entries[2].key.name);
 
   style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v21"));
   ASSERT_THAT(style, NotNull());
   ASSERT_EQ(1u, style->entries.size());
-  EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingEnd")), style->entries.front().key.name);
+  EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingEnd"), style->entries.front().key.name);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 8abd9de..1bb0696 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -52,7 +52,7 @@
   // We allow unqualified class names (ie: .HelloActivity)
   // Since we don't know the package name, we can just make a fake one here and
   // the test will be identical as long as the real package name is valid too.
-  Maybe<std::string> fully_qualified_class_name =
+  std::optional<std::string> fully_qualified_class_name =
       util::GetFullyQualifiedClassName("a", attr->value);
 
   StringPiece qualified_class_name = fully_qualified_class_name
@@ -146,7 +146,7 @@
     // Now inject the android:isFeatureSplit="true" attribute.
     xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
     if (attr != nullptr) {
-      if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
+      if (!ResourceUtils::ParseBool(attr->value).value_or(false)) {
         // The isFeatureSplit attribute is false, which conflicts with the use
         // of "featureSplit".
         diag->Error(DiagMessage(el->line_number)
@@ -523,7 +523,8 @@
                                   const StringPiece& attr_name, xml::Element* el) {
   xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name);
   if (attr != nullptr) {
-    if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) {
+    if (std::optional<std::string> new_value =
+            util::GetFullyQualifiedClassName(package, attr->value)) {
       attr->value = std::move(new_value.value());
     }
   }
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 34ad8d5..d5d1d17 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -22,7 +22,7 @@
 #include "android-base/macros.h"
 
 #include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
+
 #include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
 
@@ -30,47 +30,47 @@
 
 struct ManifestFixerOptions {
   // The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag.
-  Maybe<std::string> min_sdk_version_default;
+  std::optional<std::string> min_sdk_version_default;
 
   // The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag.
-  Maybe<std::string> target_sdk_version_default;
+  std::optional<std::string> target_sdk_version_default;
 
   // The Android package to use instead of the one defined in 'package' in <manifest>.
   // This also renames all relative package/class names in the manifest to fully qualified
   // Java names.
-  Maybe<std::string> rename_manifest_package;
+  std::optional<std::string> rename_manifest_package;
 
   // The Android package to use instead of the one defined in 'android:targetPackage' in
   // <instrumentation>.
-  Maybe<std::string> rename_instrumentation_target_package;
+  std::optional<std::string> rename_instrumentation_target_package;
 
   // The Android package to use instead of the one defined in 'android:targetPackage' in
   // <overlay>.
-  Maybe<std::string> rename_overlay_target_package;
+  std::optional<std::string> rename_overlay_target_package;
 
   // The version name to set if 'android:versionName' is not defined in <manifest> or if
   // replace_version is set.
-  Maybe<std::string> version_name_default;
+  std::optional<std::string> version_name_default;
 
   // The version code to set if 'android:versionCode' is not defined in <manifest> or if
   // replace_version is set.
-  Maybe<std::string> version_code_default;
+  std::optional<std::string> version_code_default;
 
   // The version code to set if 'android:versionCodeMajor' is not defined in <manifest> or if
   // replace_version is set.
-  Maybe<std::string> version_code_major_default;
+  std::optional<std::string> version_code_major_default;
 
   // The revision code to set if 'android:revisionCode' is not defined in <manifest> or if
   // replace_version is set.
-  Maybe<std::string> revision_code_default;
+  std::optional<std::string> revision_code_default;
 
   // The version of the framework being compiled against to set for 'android:compileSdkVersion' in
   // the <manifest> tag.
-  Maybe<std::string> compile_sdk_version;
+  std::optional<std::string> compile_sdk_version;
 
   // The version codename of the framework being compiled against to set for
   // 'android:compileSdkVersionCodename' in the <manifest> tag.
-  Maybe<std::string> compile_sdk_version_codename;
+  std::optional<std::string> compile_sdk_version_codename;
 
   // Whether validation errors should be treated only as warnings. If this is 'true', then an
   // incorrect node will not result in an error, but only as a warning, and the parsing will
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 4ac25bd..47c804c 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -190,7 +190,8 @@
  public:
   EmptyDeclStack() = default;
 
-  Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+  std::optional<xml::ExtractedPackage> TransformPackageAlias(
+      const StringPiece& alias) const override {
     if (alias.empty()) {
       return xml::ExtractedPackage{{}, true /*private*/};
     }
@@ -206,7 +207,8 @@
       : alias_namespaces_(std::move(namespaces)) {
   }
 
-  Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+  std::optional<xml::ExtractedPackage> TransformPackageAlias(
+      const StringPiece& alias) const override {
     if (alias.empty()) {
       return xml::ExtractedPackage{{}, true /*private*/};
     }
@@ -322,11 +324,11 @@
   return symbol;
 }
 
-Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
-                                                               const CallSite& callsite,
-                                                               IAaptContext* context,
-                                                               SymbolTable* symbols,
-                                                               std::string* out_error) {
+std::optional<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
+                                                                       const CallSite& callsite,
+                                                                       IAaptContext* context,
+                                                                       SymbolTable* symbols,
+                                                                       std::string* out_error) {
   const SymbolTable::Symbol* symbol =
       ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
   if (!symbol) {
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 770f1e5..b460853 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -97,11 +97,11 @@
 
   // Resolves the attribute reference and returns an xml::AaptAttribute if successful.
   // If resolution fails, outError holds the error message.
-  static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
-                                                       const CallSite& callsite,
-                                                       IAaptContext* context,
-                                                       SymbolTable* symbols,
-                                                       std::string* out_error);
+  static std::optional<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
+                                                               const CallSite& callsite,
+                                                               IAaptContext* context,
+                                                               SymbolTable* symbols,
+                                                               std::string* out_error);
 
   // Writes the resource name to the DiagMessage, using the
   // "orig_name (aka <transformed_name>)" syntax.
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 2d8f0d3..97bdd3e 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -317,12 +317,12 @@
                                                                 CallSite{"com.app.test"},
                                                                 context.get(), &table);
   ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
+  EXPECT_THAT(s->id, Eq(0x7f010000));
 
   s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
                                      context.get(), &table);
   ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
+  EXPECT_THAT(s->id, Eq(0x7f010001));
 
   EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
                                              CallSite{"com.app.bad"}, context.get(), &table),
@@ -348,7 +348,7 @@
                                                                 CallSite{"com.app.test"},
                                                                 context.get(), &table);
   ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x80010000)));
+  EXPECT_THAT(s->id, Eq(0x80010000));
 
   s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
                                      context.get(), &table);
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22f4d18..d094d36 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -303,8 +303,8 @@
           dst_config_value->value = std::move(new_file_ref);
 
         } else {
-          Maybe<std::string> original_comment = (dst_config_value->value)
-              ? dst_config_value->value->GetComment() : Maybe<std::string>();
+          auto original_comment = (dst_config_value->value)
+              ? dst_config_value->value->GetComment() : std::optional<std::string>();
 
           dst_config_value->value = src_config_value->value->Transform(cloner);
 
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 4358fb5..4cbf2d3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -409,8 +409,7 @@
 
   const auto expected = ResourceUtils::MakeBool(true);
   EXPECT_THAT(style->entries, Contains(Field(&Style::Entry::value, Pointee(ValueEq(*expected)))));
-  EXPECT_THAT(style->parent,
-              Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
+  EXPECT_THAT(style->parent, Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")));
 }
 
 TEST_F(TableMergerTest, OverrideStyleInsteadOfOverlaying) {
@@ -483,7 +482,7 @@
   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
 
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
-  Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+  std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
@@ -517,7 +516,7 @@
   ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
 
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
-  Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+  std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index aaa085e..1f8548b 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -68,7 +68,7 @@
 
       const Attribute* attribute = &default_attribute;
 
-      if (Maybe<xml::ExtractedPackage> maybe_package =
+      if (std::optional<xml::ExtractedPackage> maybe_package =
               xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
         // There is a valid package name for this attribute. We will look this up.
         Reference attr_ref(
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index ddf5b9a..6d96cf1 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -100,18 +100,18 @@
   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id);
   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
 
   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id);
   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
   ASSERT_THAT(ref, NotNull());
-  EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name);  // Make sure the name
-                                                                          // didn't change.
-  EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
+  EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name);  // Make sure the name
+                                                              // didn't change.
+  EXPECT_EQ(ResourceId(0x7f020000), ref->id);
 
   xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
   ASSERT_THAT(xml_attr, NotNull());
@@ -172,7 +172,7 @@
       view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x7f010001), xml_attr->compiled_attribute.value().id);
   EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
 }
 
@@ -190,11 +190,11 @@
   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x7f010000), xml_attr->compiled_attribute.value().id);
   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
   ASSERT_THAT(ref, NotNull());
   ASSERT_TRUE(ref->name);
-  EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
+  EXPECT_EQ(ResourceId(0x7f020001), ref->id);
 }
 
 TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
@@ -214,10 +214,10 @@
   xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x01010002), xml_attr->compiled_attribute.value().id);
   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
   ASSERT_THAT(ref, NotNull());
-  EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
+  EXPECT_EQ(ResourceId(0x01030000), ref->id);
 
   ASSERT_FALSE(view_el->GetChildElements().empty());
   view_el = view_el->GetChildElements().front();
@@ -228,10 +228,10 @@
   xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
   ref = ValueCast<Reference>(xml_attr->compiled_value.get());
   ASSERT_THAT(ref, NotNull());
-  EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+  EXPECT_EQ(ResourceId(0x7f030000), ref->id);
 }
 
 TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
@@ -250,10 +250,10 @@
   xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
   Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
   ASSERT_THAT(ref, NotNull());
-  EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+  EXPECT_EQ(ResourceId(0x7f030000), ref->id);
 }
 
 
@@ -270,7 +270,7 @@
   xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
 
   BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
   ASSERT_THAT(value, NotNull());
@@ -292,7 +292,7 @@
   xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
   ASSERT_THAT(xml_attr, NotNull());
   ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+  EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
 
   BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
   ASSERT_THAT(value, NotNull());
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index d385267..2d58cbf 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -75,8 +75,8 @@
 
   // Fill in the package name if necessary.
   // If there is no package in `name`, we will need to copy the ResourceName
-  // and store it somewhere; we use the Maybe<> class to reserve storage.
-  Maybe<ResourceName> name_with_package_impl;
+  // and store it somewhere; we use the std::optional<> class to reserve storage.
+  std::optional<ResourceName> name_with_package_impl;
   if (name.package.empty()) {
     name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry);
     name_with_package = &name_with_package_impl.value();
@@ -88,9 +88,9 @@
   }
 
   // The name was not found in the cache. Mangle it (if necessary) and find it in our sources.
-  // Again, here we use a Maybe<> object to reserve storage if we need to mangle.
+  // Again, here we use a std::optional<> object to reserve storage if we need to mangle.
   const ResourceName* mangled_name = name_with_package;
-  Maybe<ResourceName> mangled_name_impl;
+  std::optional<ResourceName> mangled_name_impl;
   if (mangler_->ShouldMangle(name_with_package->package)) {
     mangled_name_impl = mangler_->MangleName(*name_with_package);
     mangled_name = &mangled_name_impl.value();
@@ -183,7 +183,7 @@
 
 std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
     const ResourceName& name) {
-  Maybe<ResourceTable::SearchResult> result = table_->FindResource(name);
+  std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
   if (!result) {
     if (name.type == ResourceType::kAttr) {
       // Recurse and try looking up a private attribute.
@@ -306,7 +306,7 @@
         return nullptr;
       }
 
-      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
+      std::optional<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
       if (!parsed_name) {
         return nullptr;
       }
@@ -382,8 +382,7 @@
   return {};
 }
 
-static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
-                                           ResourceId id) {
+static std::optional<ResourceName> GetResourceName(android::AssetManager2& am, ResourceId id) {
   auto name = am.GetResourceName(id.id);
   if (!name.has_value()) {
     return {};
@@ -402,7 +401,7 @@
     return {};
   }
 
-  Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
+  std::optional<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
   if (!maybe_name) {
     return {};
   }
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 06eaf63..65ae7be 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -56,7 +56,7 @@
   struct Symbol {
     Symbol() = default;
 
-    explicit Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
+    explicit Symbol(const std::optional<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
                     bool pub = false)
         : id(i), attribute(attr), is_public(pub) {
     }
@@ -66,7 +66,7 @@
     Symbol& operator=(const Symbol&) = default;
     Symbol& operator=(Symbol&&) = default;
 
-    Maybe<ResourceId> id;
+    std::optional<ResourceId> id;
     std::shared_ptr<Attribute> attribute;
     bool is_public = false;
     bool is_dynamic = false;
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 4816596..23331de 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -159,7 +159,8 @@
   return std::move(table_);
 }
 
-std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) {
+std::unique_ptr<Reference> BuildReference(const StringPiece& ref,
+                                          const std::optional<ResourceId>& id) {
   std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref));
   reference->id = id;
   return reference;
@@ -218,7 +219,8 @@
   return std::move(style_);
 }
 
-StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) {
+StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str,
+                                            const std::optional<ResourceId>& id) {
   styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
   styleable_->entries.back().id = id;
   return *this;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 3ff955d..55778ae 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -29,7 +29,6 @@
 #include "configuration/ConfigurationParser.internal.h"
 #include "process/IResourceTableConsumer.h"
 #include "test/Common.h"
-#include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
 namespace aapt {
@@ -86,7 +85,7 @@
 };
 
 std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
-                                          const Maybe<ResourceId>& id = {});
+                                          const std::optional<ResourceId>& id = {});
 std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
 
 template <typename T>
@@ -149,7 +148,8 @@
 class StyleableBuilder {
  public:
   StyleableBuilder() = default;
-  StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
+  StyleableBuilder& AddItem(const android::StringPiece& str,
+                            const std::optional<ResourceId>& id = {});
   std::unique_ptr<Styleable> Build();
 
  private:
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
index 23c2218..e029d02 100644
--- a/tools/aapt2/test/Common.cpp
+++ b/tools/aapt2/test/Common.cpp
@@ -48,7 +48,7 @@
                                           const android::StringPiece& res_name,
                                           const ConfigDescription& config,
                                           const android::StringPiece& product) {
-  Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+  std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
   if (result) {
     ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
     if (config_value) {
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 777ca5c..7006964 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -55,7 +55,7 @@
 T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name,
                                const android::ConfigDescription& config,
                                const android::StringPiece& product) {
-  Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+  std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
   if (result) {
     ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
     if (config_value) {
@@ -130,7 +130,7 @@
 
 // Add a print method to Maybe.
 template <typename T>
-void PrintTo(const Maybe<T>& value, std::ostream* out) {
+void PrintTo(const std::optional<T>& value, std::ostream* out) {
   if (value) {
     *out << ::testing::PrintToString(value.value());
   } else {
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 5d8ded3..e1b8dd5 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -95,8 +95,8 @@
   friend class ContextBuilder;
 
   PackageType package_type_ = PackageType::kApp;
-  Maybe<std::string> compilation_package_;
-  Maybe<uint8_t> package_id_;
+  std::optional<std::string> compilation_package_;
+  std::optional<uint8_t> package_id_;
   StdErrDiagnostics diagnostics_;
   NameMangler name_mangler_;
   SymbolTable symbols_;
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 285e5a1..e2f71dc 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -126,7 +126,7 @@
   link_args.insert(link_args.end(), {"-I", android_sdk});
 
   // Add the files from the compiled resources directory to the link file arguments
-  Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
+  std::optional<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
   if (compiled_files) {
     for (std::string& compile_file : compiled_files.value()) {
       compile_file = file::BuildPath({flat_dir, compile_file});
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d57de6..5d2eda3 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -50,12 +50,12 @@
 FileType GetFileType(const std::string& path) {
   std::wstring path_utf16;
   if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
-    return FileType::kNonexistant;
+    return FileType::kNonExistant;
   }
 
   DWORD result = GetFileAttributesW(path_utf16.c_str());
   if (result == INVALID_FILE_ATTRIBUTES) {
-    return FileType::kNonexistant;
+    return FileType::kNonExistant;
   }
 
   if (result & FILE_ATTRIBUTE_DIRECTORY) {
@@ -72,7 +72,7 @@
 
   if (result == -1) {
     if (errno == ENOENT || errno == ENOTDIR) {
-      return FileType::kNonexistant;
+      return FileType::kNonExistant;
     }
     return FileType::kUnknown;
   }
@@ -208,7 +208,7 @@
   return out_path;
 }
 
-Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) {
+std::optional<FileMap> MmapPath(const std::string& path, std::string* out_error) {
   int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
   unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
   if (fd == -1) {
@@ -344,8 +344,8 @@
   return true;
 }
 
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
-                                          const FileFilter* filter) {
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+                                                  IDiagnostics* diag, const FileFilter* filter) {
   const std::string root_dir = path.to_string();
   std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
   if (!d) {
@@ -382,7 +382,7 @@
   for (const std::string& subdir : subdirs) {
     std::string full_subdir = root_dir;
     AppendPath(&full_subdir, subdir);
-    Maybe<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
+    std::optional<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
     if (!subfiles) {
       return {};
     }
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 481a4cd..877cd56 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -18,6 +18,7 @@
 #define AAPT_FILES_H
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <unordered_set>
 #include <vector>
@@ -27,7 +28,6 @@
 #include "utils/FileMap.h"
 
 #include "Diagnostics.h"
-#include "Maybe.h"
 #include "Source.h"
 
 namespace aapt {
@@ -43,7 +43,7 @@
 
 enum class FileType {
   kUnknown = 0,
-  kNonexistant,
+  kNonExistant,
   kRegular,
   kDirectory,
   kCharDev,
@@ -81,7 +81,7 @@
 std::string PackageToPath(const android::StringPiece& package);
 
 // Creates a FileMap for the file at path.
-Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
+std::optional<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
 
 // Reads the file at path and appends each line to the outArgList vector.
 bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
@@ -124,8 +124,9 @@
 
 // Returns a list of files relative to the directory identified by `path`.
 // An optional FileFilter filters out any files that don't pass.
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
-                                          const FileFilter* filter = nullptr);
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+                                                  IDiagnostics* diag,
+                                                  const FileFilter* filter = nullptr);
 
 }  // namespace file
 }  // namespace aapt
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
deleted file mode 100644
index 047e1a5..0000000
--- a/tools/aapt2/util/Maybe.h
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_MAYBE_H
-#define AAPT_MAYBE_H
-
-#include <type_traits>
-#include <utility>
-
-#include "android-base/logging.h"
-
-#include "util/TypeTraits.h"
-
-namespace aapt {
-
-/**
- * Either holds a valid value of type T, or holds Nothing.
- * The value is stored inline in this structure, so no
- * heap memory is used when creating a Maybe<T> object.
- */
-template <typename T>
-class Maybe {
- public:
-  /**
-   * Construct Nothing.
-   */
-  Maybe();
-
-  ~Maybe();
-
-  Maybe(const Maybe& rhs);
-
-  template <typename U>
-  Maybe(const Maybe<U>& rhs);  // NOLINT(google-explicit-constructor)
-
-  Maybe(Maybe&& rhs) noexcept;
-
-  template <typename U>
-  Maybe(Maybe<U>&& rhs);  // NOLINT(google-explicit-constructor)
-
-  Maybe& operator=(const Maybe& rhs);
-
-  template <typename U>
-  Maybe& operator=(const Maybe<U>& rhs);
-
-  Maybe& operator=(Maybe&& rhs) noexcept;
-
-  template <typename U>
-  Maybe& operator=(Maybe<U>&& rhs);
-
-  /**
-   * Construct a Maybe holding a value.
-   */
-  Maybe(const T& value);  // NOLINT(google-explicit-constructor)
-
-  /**
-   * Construct a Maybe holding a value.
-   */
-  Maybe(T&& value);  // NOLINT(google-explicit-constructor)
-
-  /**
-   * True if this holds a value, false if
-   * it holds Nothing.
-   */
-  explicit operator bool() const;
-
-  /**
-   * Gets the value if one exists, or else
-   * panics.
-   */
-  T& value();
-
-  /**
-   * Gets the value if one exists, or else
-   * panics.
-   */
-  const T& value() const;
-
-  T value_or_default(const T& def) const;
-
- private:
-  template <typename U>
-  friend class Maybe;
-
-  template <typename U>
-  Maybe& copy(const Maybe<U>& rhs);
-
-  template <typename U>
-  Maybe& move(Maybe<U>&& rhs);
-
-  void destroy();
-
-  bool nothing_;
-
-  typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
-};
-
-template <typename T>
-Maybe<T>::Maybe() : nothing_(true) {}
-
-template <typename T>
-Maybe<T>::~Maybe() {
-  if (!nothing_) {
-    destroy();
-  }
-}
-
-template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) {
-  if (!rhs.nothing_) {
-    new (&storage_) T(reinterpret_cast<const T&>(rhs.storage_));
-  }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) {
-  if (!rhs.nothing_) {
-    new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
-  }
-}
-
-template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) {
-  if (!rhs.nothing_) {
-    rhs.nothing_ = true;
-
-    // Move the value from rhs.
-    new (&storage_) T(std::move(reinterpret_cast<T&>(rhs.storage_)));
-    rhs.destroy();
-  }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs) : nothing_(rhs.nothing_) {
-  if (!rhs.nothing_) {
-    rhs.nothing_ = true;
-
-    // Move the value from rhs.
-    new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
-    rhs.destroy();
-  }
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
-  // Delegate to the actual assignment.
-  return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
-  return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
-  if (nothing_ && rhs.nothing_) {
-    // Both are nothing, nothing to do.
-    return *this;
-  } else if (!nothing_ && !rhs.nothing_) {
-    // We both are something, so assign rhs to us.
-    reinterpret_cast<T&>(storage_) = reinterpret_cast<const U&>(rhs.storage_);
-  } else if (nothing_) {
-    // We are nothing but rhs is something.
-    nothing_ = rhs.nothing_;
-
-    // Copy the value from rhs.
-    new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
-  } else {
-    // We are something but rhs is nothing, so destroy our value.
-    nothing_ = rhs.nothing_;
-    destroy();
-  }
-  return *this;
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept {
-  // Delegate to the actual assignment.
-  return move(std::forward<Maybe<T>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
-  return move(std::forward<Maybe<U>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
-  if (nothing_ && rhs.nothing_) {
-    // Both are nothing, nothing to do.
-    return *this;
-  } else if (!nothing_ && !rhs.nothing_) {
-    // We both are something, so move assign rhs to us.
-    rhs.nothing_ = true;
-    reinterpret_cast<T&>(storage_) =
-        std::move(reinterpret_cast<U&>(rhs.storage_));
-    rhs.destroy();
-  } else if (nothing_) {
-    // We are nothing but rhs is something.
-    nothing_ = false;
-    rhs.nothing_ = true;
-
-    // Move the value from rhs.
-    new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
-    rhs.destroy();
-  } else {
-    // We are something but rhs is nothing, so destroy our value.
-    nothing_ = true;
-    destroy();
-  }
-  return *this;
-}
-
-template <typename T>
-Maybe<T>::Maybe(const T& value) : nothing_(false) {
-  new (&storage_) T(value);
-}
-
-template <typename T>
-Maybe<T>::Maybe(T&& value) : nothing_(false) {
-  new (&storage_) T(std::forward<T>(value));
-}
-
-template <typename T>
-Maybe<T>::operator bool() const {
-  return !nothing_;
-}
-
-template <typename T>
-T& Maybe<T>::value() {
-  CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
-  return reinterpret_cast<T&>(storage_);
-}
-
-template <typename T>
-const T& Maybe<T>::value() const {
-  CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
-  return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-T Maybe<T>::value_or_default(const T& def) const {
-  if (nothing_) {
-    return def;
-  }
-  return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-void Maybe<T>::destroy() {
-  reinterpret_cast<T&>(storage_).~T();
-}
-
-template <typename T>
-inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
-  return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
-}
-
-template <typename T>
-inline Maybe<T> make_nothing() {
-  return Maybe<T>();
-}
-
-// Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
-// That way the compiler will show an error at the callsite when comparing two Maybe<> objects
-// whose inner types can't be compared.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
-                                                                       const Maybe<U>& b) {
-  if (a && b) {
-    return a.value() == b.value();
-  } else if (!a && !b) {
-    return true;
-  }
-  return false;
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
-                                                                       const U& b) {
-  return a ? a.value() == b : false;
-}
-
-// Same as operator== but negated.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a,
-                                                                       const Maybe<U>& b) {
-  return !(a == b);
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a,
-                                                                      const Maybe<U>& b) {
-  if (a && b) {
-    return a.value() < b.value();
-  } else if (!a && !b) {
-    return false;
-  }
-  return !a;
-}
-
-}  // namespace aapt
-
-#endif  // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
deleted file mode 100644
index 4c921f1..0000000
--- a/tools/aapt2/util/Maybe_test.cpp
+++ /dev/null
@@ -1,129 +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.
- */
-
-#include "util/Maybe.h"
-
-#include <string>
-
-#include "test/Test.h"
-
-namespace aapt {
-
-struct Fake {
-  Fake() {
-    data = new int;
-    *data = 1;
-    std::cerr << "Construct Fake{0x" << (void*)this << "} with data=0x"
-              << (void*)data << std::endl;
-  }
-
-  Fake(const Fake& rhs) {
-    data = nullptr;
-    if (rhs.data) {
-      data = new int;
-      *data = *rhs.data;
-    }
-    std::cerr << "CopyConstruct Fake{0x" << (void*)this << "} from Fake{0x"
-              << (const void*)&rhs << "}" << std::endl;
-  }
-
-  Fake(Fake&& rhs) {
-    data = rhs.data;
-    rhs.data = nullptr;
-    std::cerr << "MoveConstruct Fake{0x" << (void*)this << "} from Fake{0x"
-              << (const void*)&rhs << "}" << std::endl;
-  }
-
-  Fake& operator=(const Fake& rhs) {
-    delete data;
-    data = nullptr;
-
-    if (rhs.data) {
-      data = new int;
-      *data = *rhs.data;
-    }
-    std::cerr << "CopyAssign Fake{0x" << (void*)this << "} from Fake{0x"
-              << (const void*)&rhs << "}" << std::endl;
-    return *this;
-  }
-
-  Fake& operator=(Fake&& rhs) {
-    delete data;
-    data = rhs.data;
-    rhs.data = nullptr;
-    std::cerr << "MoveAssign Fake{0x" << (void*)this << "} from Fake{0x"
-              << (const void*)&rhs << "}" << std::endl;
-    return *this;
-  }
-
-  ~Fake() {
-    std::cerr << "Destruct Fake{0x" << (void*)this << "} with data=0x"
-              << (void*)data << std::endl;
-    delete data;
-  }
-
-  int* data;
-};
-
-TEST(MaybeTest, MakeNothing) {
-  Maybe<int> val = make_nothing<int>();
-  EXPECT_FALSE(val);
-
-  Maybe<std::string> val2 = make_nothing<std::string>();
-  EXPECT_FALSE(val2);
-
-  val2 = make_nothing<std::string>();
-  EXPECT_FALSE(val2);
-}
-
-TEST(MaybeTest, MakeSomething) {
-  Maybe<int> val = make_value(23);
-  ASSERT_TRUE(val);
-  EXPECT_EQ(23, val.value());
-
-  Maybe<std::string> val2 = make_value(std::string("hey"));
-  ASSERT_TRUE(val2);
-  EXPECT_EQ(std::string("hey"), val2.value());
-}
-
-TEST(MaybeTest, Lifecycle) {
-  Maybe<Fake> val = make_nothing<Fake>();
-
-  Maybe<Fake> val2 = make_value(Fake());
-}
-
-TEST(MaybeTest, MoveAssign) {
-  Maybe<Fake> val;
-  {
-    Maybe<Fake> val2 = Fake();
-    val = std::move(val2);
-  }
-}
-
-TEST(MaybeTest, Equality) {
-  Maybe<int> a = 1;
-  Maybe<int> b = 1;
-  Maybe<int> c;
-
-  Maybe<int> emptyA, emptyB;
-
-  EXPECT_EQ(a, b);
-  EXPECT_EQ(b, a);
-  EXPECT_NE(a, c);
-  EXPECT_EQ(emptyA, emptyB);
-}
-
-}  // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d7a8e6f..44b4ec1 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -28,7 +28,6 @@
 #include "text/Unicode.h"
 #include "text/Utf8Iterator.h"
 #include "util/BigBuffer.h"
-#include "util/Maybe.h"
 #include "utils/Unicode.h"
 
 using ::aapt::text::Utf8Iterator;
@@ -193,8 +192,8 @@
   return IsAndroidNameImpl(str) > 0;
 }
 
-Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
-                                              const StringPiece& classname) {
+std::optional<std::string> GetFullyQualifiedClassName(const StringPiece& package,
+                                                      const StringPiece& classname) {
   if (classname.empty()) {
     return {};
   }
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c77aca3..c3efe6a 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -28,7 +28,6 @@
 #include "utils/ByteOrder.h"
 
 #include "util/BigBuffer.h"
-#include "util/Maybe.h"
 
 #ifdef _WIN32
 // TODO(adamlesinski): remove once http://b/32447322 is resolved.
@@ -105,8 +104,8 @@
 // .asdf        --> package.asdf
 // .a.b         --> package.a.b
 // asdf.adsf    --> asdf.adsf
-Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
-                                              const android::StringPiece& class_name);
+std::optional<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
+                                                      const android::StringPiece& class_name);
 
 // Retrieves the formatted name of aapt2.
 const char* GetToolName();
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 2cdcfe4..8b7eadf9 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -545,7 +545,7 @@
 void PackageAwareVisitor::BeforeVisitElement(Element* el) {
   std::vector<PackageDecl> decls;
   for (const NamespaceDecl& decl : el->namespace_decls) {
-    if (Maybe<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
+    if (std::optional<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
       decls.push_back(PackageDecl{decl.prefix, std::move(maybe_package.value())});
     }
   }
@@ -556,7 +556,8 @@
   package_decls_.pop_back();
 }
 
-Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
+    const StringPiece& alias) const {
   if (alias.empty()) {
     return ExtractedPackage{{}, false /*private*/};
   }
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index a5b2d10..5d31804 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -65,12 +65,12 @@
 };
 
 struct AaptAttribute {
-  explicit AaptAttribute(const ::aapt::Attribute& attr, const Maybe<ResourceId>& resid = {})
+  explicit AaptAttribute(const ::aapt::Attribute& attr, const std::optional<ResourceId>& resid = {})
       : attribute(attr), id(resid) {
   }
 
   aapt::Attribute attribute;
-  Maybe<ResourceId> id;
+  std::optional<ResourceId> id;
 };
 
 // An XML attribute.
@@ -79,7 +79,7 @@
   std::string name;
   std::string value;
 
-  Maybe<AaptAttribute> compiled_attribute;
+  std::optional<AaptAttribute> compiled_attribute;
   std::unique_ptr<Item> compiled_value;
 };
 
@@ -235,7 +235,8 @@
  public:
   using Visitor::Visit;
 
-  Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+  std::optional<ExtractedPackage> TransformPackageAlias(
+      const android::StringPiece& alias) const override;
 
  protected:
   PackageAwareVisitor() = default;
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index ca46d53..6c717dc 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -98,7 +98,7 @@
   // the Attribute accepts (eg: string|reference).
   ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute);
   EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id,
-              Eq(make_value(ResourceId(0x01010001u))));
+              Eq(ResourceId(0x01010001u)));
 
   EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo"));
   EXPECT_THAT(new_doc->root->attributes[0].compiled_value,
@@ -145,21 +145,19 @@
 
   void Visit(Element* el) override {
     if (el->name == "View1") {
-      EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+      EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
     } else if (el->name == "View2") {
-      EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
-      EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
+      EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+      EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
     } else if (el->name == "View3") {
-      EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
-      EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
-      EXPECT_THAT(TransformPackageAlias("three"),
-                  Eq(make_value(ExtractedPackage{"com.three", false})));
+      EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+      EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+      EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
     } else if (el->name == "View4") {
-      EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
-      EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
-      EXPECT_THAT(TransformPackageAlias("three"),
-                  Eq(make_value(ExtractedPackage{"com.three", false})));
-      EXPECT_THAT(TransformPackageAlias("four"), Eq(make_value(ExtractedPackage{"", true})));
+      EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+      EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+      EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
+      EXPECT_THAT(TransformPackageAlias("four"), Eq(ExtractedPackage{"", true}));
     }
   }
 };
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 182203d..bfa0749 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -17,7 +17,6 @@
 #include <iostream>
 #include <string>
 
-#include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlPullParser.h"
 #include "xml/XmlUtil.h"
@@ -84,8 +83,7 @@
   // handling of references that use namespace aliases.
   if (next_event == Event::kStartNamespace ||
       next_event == Event::kEndNamespace) {
-    Maybe<ExtractedPackage> result =
-        ExtractPackageFromNamespace(namespace_uri());
+    std::optional<ExtractedPackage> result = ExtractPackageFromNamespace(namespace_uri());
     if (next_event == Event::kStartNamespace) {
       if (result) {
         package_aliases_.emplace_back(
@@ -142,7 +140,8 @@
   return event_queue_.front().data2;
 }
 
-Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> XmlPullParser::TransformPackageAlias(
+    const StringPiece& alias) const {
   if (alias.empty()) {
     return ExtractedPackage{{}, false /*private*/};
   }
@@ -308,8 +307,7 @@
                                       parser->depth_ });
 }
 
-Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
-                                 const StringPiece& name) {
+std::optional<StringPiece> FindAttribute(const XmlPullParser* parser, const StringPiece& name) {
   auto iter = parser->FindAttribute("", name);
   if (iter != parser->end_attributes()) {
     return StringPiece(util::TrimWhitespace(iter->value));
@@ -317,8 +315,8 @@
   return {};
 }
 
-Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
-                                         const StringPiece& name) {
+std::optional<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+                                                 const StringPiece& name) {
   auto iter = parser->FindAttribute("", name);
   if (iter != parser->end_attributes()) {
     StringPiece trimmed = util::TrimWhitespace(iter->value);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 5da2d4b..ab34772 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -33,7 +33,6 @@
 #include "Resource.h"
 #include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
 #include "xml/XmlUtil.h"
 
 namespace aapt {
@@ -121,7 +120,8 @@
    * If xmlns:app="http://schemas.android.com/apk/res-auto", then
    * 'package' will be set to 'defaultPackage'.
    */
-  Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+  std::optional<ExtractedPackage> TransformPackageAlias(
+      const android::StringPiece& alias) const override;
 
   struct PackageDecl {
     std::string prefix;
@@ -193,16 +193,16 @@
 /**
  * Finds the attribute in the current element within the global namespace.
  */
-Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
-                                          const android::StringPiece& name);
+std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser,
+                                                  const android::StringPiece& name);
 
 /**
  * Finds the attribute in the current element within the global namespace. The
  * attribute's value
  * must not be the empty string.
  */
-Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
-                                                  const android::StringPiece& name);
+std::optional<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+                                                          const android::StringPiece& name);
 
 //
 // Implementation
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index 0a622b2..114b5ba 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -19,7 +19,6 @@
 #include <algorithm>
 #include <string>
 
-#include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlDom.h"
 
@@ -34,8 +33,7 @@
   return result;
 }
 
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(
-    const std::string& namespace_uri) {
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri) {
   if (util::StartsWith(namespace_uri, kSchemaPublicPrefix)) {
     StringPiece schema_prefix = kSchemaPublicPrefix;
     StringPiece package = namespace_uri;
@@ -62,7 +60,7 @@
 
 void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
   if (in_ref->name) {
-    if (Maybe<ExtractedPackage> transformed_package =
+    if (std::optional<ExtractedPackage> transformed_package =
             decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
       ExtractedPackage& extracted_package = transformed_package.value();
       in_ref->name.value().package = std::move(extracted_package.package);
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 592a604..1ab05a9 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -20,7 +20,6 @@
 #include <string>
 
 #include "ResourceValues.h"
-#include "util/Maybe.h"
 
 namespace aapt {
 namespace xml {
@@ -53,7 +52,7 @@
 //
 // Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
 // package name.
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
 
 // Returns an XML Android namespace for the given package of the form:
 //   http://schemas.android.com/apk/res/<package>
@@ -69,7 +68,7 @@
   virtual ~IPackageDeclStack() = default;
 
   // Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
-  virtual Maybe<ExtractedPackage> TransformPackageAlias(
+  virtual std::optional<ExtractedPackage> TransformPackageAlias(
       const android::StringPiece& alias) const = 0;
 };
 
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index cbded8f..7b6ce9e 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -27,7 +27,7 @@
   ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
   ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/"));
 
-  Maybe<xml::ExtractedPackage> p =
+  std::optional<xml::ExtractedPackage> p =
       xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/a");
   ASSERT_TRUE(p);
   EXPECT_EQ(std::string("a"), p.value().package);
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 81d35ef..3cdb27c 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -8,7 +8,21 @@
     # Change is explicitly marked as ok to skip AOSP
     exit 0
 else
-    # Change appears to be non-AOSP; search for files
+    # Change appears to be non-AOSP.
+
+    # If this is a cherry-pick, then allow it.
+    cherrypick=0
+    while read -r line ; do
+      if [[ $line =~ cherry\ picked\ from  ]] ; then
+        (( cherrypick++ ))
+      fi
+    done < <(git show $1)
+    if (( cherrypick != 0 )); then
+      # This is a cherry-pick, so allow it.
+      exit 0
+    fi
+
+    # See if any files are affected.
     count=0
     while read -r file ; do
         if (( count == 0 )); then
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa784..9ceb204 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
  * cccc dd
  */
 fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
-    val col1w = map { (a, _) -> a.length }.max()!!
-    val col2w = map { (_, b) -> b.length }.max()!!
+    val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+    val col2w = map { (_, b) -> b.length }.maxOrNull()!!
     return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
 }
 
diff --git a/tools/lint/Android.bp b/tools/lint/Android.bp
new file mode 100644
index 0000000..dcbc32b
--- /dev/null
+++ b/tools/lint/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+    name: "AndroidFrameworkLintChecker",
+    srcs: ["checks/src/main/java/**/*.kt"],
+    plugins: ["auto_service_plugin"],
+    libs: [
+        "auto_service_annotations",
+        "lint_api",
+    ],
+}
+
+// TODO: (b/162368644) Implement these (working in gradle) Kotlin Tests to run on Soong
+//java_test_host {
+//    name: "AndroidFrameworkLintCheckerTest",
+//    srcs: [
+//     "checks/src/test/java/**/*.kt",
+//     "checks/src/main/java/**/*.kt",
+//    ],
+//    plugins: ["auto_service_plugin"],
+//    static_libs: [
+//        "auto_service_annotations",
+//        "lint_api",
+//    ],
+//}
diff --git a/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt
new file mode 100644
index 0000000..a41d65d
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenDetector.kt
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.lint
+
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NESTED_CLEAR_IDENTITY_CALLS
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NON_FINAL_TOKEN
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_UNUSED_TOKEN
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageClearIdentityCallNotFollowedByTryFinally
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsPrimary
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsSecondary
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNonFinalToken
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageRestoreIdentityCallNotInFinallyBlock
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUnusedToken
+import com.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.search.PsiSearchScopeUtil
+import com.intellij.psi.search.SearchScope
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UDeclarationsExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.ULocalVariable
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.UTryExpression
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Lint Detector that finds issues with improper usages of the token returned by
+ * Binder.clearCallingIdentity()
+ */
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
+    private companion object {
+        const val CLASS_BINDER = "android.os.Binder"
+        const val CLASS_USER_HANDLE = "android.os.UserHandle"
+
+        @JvmField
+        val callerAwareMethods = listOf(
+                Method.BINDER_GET_CALLING_PID,
+                Method.BINDER_GET_CALLING_UID,
+                Method.BINDER_GET_CALLING_UID_OR_THROW,
+                Method.BINDER_GET_CALLING_USER_HANDLE,
+                Method.USER_HANDLE_GET_CALLING_APP_ID,
+                Method.USER_HANDLE_GET_CALLING_USER_ID
+        )
+    }
+
+    /** Map of <Token variable name, Token object> */
+    private val tokensMap = mutableMapOf<String, Token>()
+
+    override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+            listOf(ULocalVariable::class.java, UQualifiedReferenceExpression::class.java)
+
+    override fun createUastHandler(context: JavaContext): UElementHandler =
+            TokenUastHandler(context)
+
+    /** File analysis starts with a clear map */
+    override fun beforeCheckFile(context: Context) {
+        tokensMap.clear()
+    }
+
+    /**
+     * - If tokensMap has tokens after checking the file -> reports all locations as unused token
+     * issue incidents
+     * - File analysis ends with a clear map
+     */
+    override fun afterCheckFile(context: Context) {
+        for (token in tokensMap.values) {
+            context.report(
+                    ISSUE_UNUSED_TOKEN,
+                    token.location,
+                    getIncidentMessageUnusedToken(token.variableName)
+            )
+        }
+        tokensMap.clear()
+    }
+
+    /** UAST handler that analyses elements and reports incidents */
+    private inner class TokenUastHandler(val context: JavaContext) : UElementHandler() {
+        /**
+         * For every variable initialization with Binder.clearCallingIdentity():
+         * - Checks for non-final token issue
+         * - Checks for unused token issue within different scopes
+         * - Checks for nested calls of clearCallingIdentity() issue
+         * - Checks for clearCallingIdentity() not followed by try-finally issue
+         * - Stores token variable name, scope in the file, location and finally block in tokensMap
+         */
+        override fun visitLocalVariable(node: ULocalVariable) {
+            val rhsExpression = node.uastInitializer as? UQualifiedReferenceExpression ?: return
+            if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
+            val location = context.getLocation(node as UElement)
+            val variableName = node.getName()
+            if (!node.isFinal) {
+                context.report(
+                        ISSUE_NON_FINAL_TOKEN,
+                        location,
+                        getIncidentMessageNonFinalToken(variableName)
+                )
+            }
+            // If there exists an unused variable with the same name in the map, we can imply that
+            // we left the scope of the previous declaration, so we need to report the unused token
+            val oldToken = tokensMap[variableName]
+            if (oldToken != null) {
+                context.report(
+                        ISSUE_UNUSED_TOKEN,
+                        oldToken.location,
+                        getIncidentMessageUnusedToken(oldToken.variableName)
+                )
+            }
+            // If there exists a token in the same scope as the current new token, it means that
+            // clearCallingIdentity() has been called at least twice without immediate restoration
+            // of identity, so we need to report the nested call of clearCallingIdentity()
+            val firstCallToken = findFirstTokenInScope(node)
+            if (firstCallToken != null) {
+                context.report(
+                        ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+                        createNestedLocation(firstCallToken, location),
+                        getIncidentMessageNestedClearIdentityCallsPrimary(
+                                firstCallToken.variableName,
+                                variableName
+                        )
+                )
+            }
+            // If the next statement in the tree is not a try-finally statement, we need to report
+            // the "clearCallingIdentity() is not followed by try-finally" issue
+            val finallyClause = (getNextStatementOfLocalVariable(node) as? UTryExpression)
+                    ?.finallyClause
+            if (finallyClause == null) {
+                context.report(
+                        ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+                        location,
+                        getIncidentMessageClearIdentityCallNotFollowedByTryFinally(variableName)
+                )
+            }
+            tokensMap[variableName] = Token(
+                    variableName,
+                    node.sourcePsi?.getUseScope(),
+                    location,
+                    finallyClause
+            )
+        }
+
+        /**
+         * For every class.method():
+         * - Checks use of caller-aware methods issue
+         * For every call of Binder.restoreCallingIdentity(token):
+         * - Checks for restoreCallingIdentity() not in the finally block issue
+         * - Removes token from tokensMap if token is within the scope of the method
+         */
+        override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
+            val token = findFirstTokenInScope(node)
+            if (isCallerAwareMethod(node) && token != null) {
+                context.report(
+                        ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+                        context.getLocation(node),
+                        getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+                                token.variableName,
+                                node.asRenderString()
+                        )
+                )
+                return
+            }
+            if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
+            val selector = node.selector as UCallExpression
+            val arg = selector.valueArguments[0] as? USimpleNameReferenceExpression ?: return
+            val variableName = arg.identifier
+            val originalScope = tokensMap[variableName]?.scope ?: return
+            val psi = arg.sourcePsi ?: return
+            // Checks if Binder.restoreCallingIdentity(token) is called within the scope of the
+            // token declaration. If not within the scope, no action is needed because the token is
+            // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
+            if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
+            // We do not report "restore identity call not in finally" issue when there is no
+            // finally block because that case is already handled by "clear identity call not
+            // followed by try-finally" issue
+            if (tokensMap[variableName]?.finallyBlock != null &&
+                    node.uastParent != tokensMap[variableName]?.finallyBlock) {
+                context.report(
+                        ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+                        context.getLocation(node),
+                        getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName)
+                )
+            }
+            tokensMap.remove(variableName)
+        }
+
+        private fun isCallerAwareMethod(expression: UQualifiedReferenceExpression): Boolean =
+                callerAwareMethods.any { method -> isMethodCall(expression, method) }
+
+        private fun isMethodCall(
+            expression: UQualifiedReferenceExpression,
+            method: Method
+        ): Boolean {
+            val psiMethod = expression.resolve() as? PsiMethod ?: return false
+            return psiMethod.getName() == method.methodName &&
+                    context.evaluator.methodMatches(
+                            psiMethod,
+                            method.className,
+                            /* allowInherit */ true,
+                            *method.args
+                    )
+        }
+
+        /**
+         * ULocalVariable in the file tree:
+         *
+         * UBlockExpression
+         *     UDeclarationsExpression
+         *         ULocalVariable
+         *         ULocalVariable
+         *     UTryStatement
+         *     etc.
+         *
+         * To get the next statement of ULocalVariable:
+         * - If there exists a next sibling in UDeclarationsExpression, return the sibling
+         * - If there exists a next sibling of UDeclarationsExpression in UBlockExpression, return
+         *   the sibling
+         * - Otherwise, return null
+         *
+         * Example 1 - the next sibling is in UDeclarationsExpression:
+         * Code:
+         * {
+         *     int num1 = 0, num2 = methodThatThrowsException();
+         * }
+         * Returns: num2 = methodThatThrowsException()
+         *
+         * Example 2 - the next sibling is in UBlockExpression:
+         * Code:
+         * {
+         *     int num1 = 0;
+         *     methodThatThrowsException();
+         * }
+         * Returns: methodThatThrowsException()
+         *
+         * Example 3 - no next sibling;
+         * Code:
+         * {
+         *     int num1 = 0;
+         * }
+         * Returns: null
+         */
+        private fun getNextStatementOfLocalVariable(node: ULocalVariable): UElement? {
+            val declarationsExpression = node.uastParent as? UDeclarationsExpression ?: return null
+            val declarations = declarationsExpression.declarations
+            val indexInDeclarations = declarations.indexOf(node)
+            if (indexInDeclarations != -1 && declarations.size > indexInDeclarations + 1) {
+                return declarations[indexInDeclarations + 1]
+            }
+            val enclosingBlock = node
+                    .getParentOfType<UBlockExpression>(strict = true) ?: return null
+            val expressions = enclosingBlock.expressions
+            val indexInBlock = expressions.indexOf(declarationsExpression as UElement)
+            return if (indexInBlock == -1) null else expressions.getOrNull(indexInBlock + 1)
+        }
+    }
+
+    private fun findFirstTokenInScope(node: UElement): Token? {
+        val psi = node.sourcePsi ?: return null
+        for (token in tokensMap.values) {
+            if (token.scope != null && PsiSearchScopeUtil.isInScope(token.scope, psi)) {
+                return token
+            }
+        }
+        return null
+    }
+
+    /**
+     * Creates a new instance of the primary location with the secondary location
+     *
+     * Here, secondary location is the helper location that shows where the issue originated
+     *
+     * The detector reports locations as objects, so when we add a secondary location to a location
+     * that has multiple issues, the secondary location gets displayed every time a location is
+     * referenced.
+     *
+     * Example:
+     * 1: final long token1 = Binder.clearCallingIdentity();
+     * 2: long token2 = Binder.clearCallingIdentity();
+     * 3: Binder.restoreCallingIdentity(token1);
+     * 4: Binder.restoreCallingIdentity(token2);
+     *
+     * Explanation:
+     * token2 has 2 issues: NonFinal and NestedCalls
+     *
+     *     Lint report without cloning                        Lint report with cloning
+     * line 2: [NonFinalIssue]                            line 2: [NonFinalIssue]
+     *     line 1: [NestedCallsIssue]
+     * line 2: [NestedCallsIssue]                            line 2: [NestedCallsIssue]
+     *     line 1: [NestedCallsIssue]                           line 1: [NestedCallsIssue]
+     */
+    private fun createNestedLocation(
+        firstCallToken: Token,
+        secondCallTokenLocation: Location
+    ): Location {
+        return cloneLocation(secondCallTokenLocation)
+                .withSecondary(
+                        cloneLocation(firstCallToken.location),
+                        getIncidentMessageNestedClearIdentityCallsSecondary(
+                                firstCallToken.variableName
+                        )
+                )
+    }
+
+    private fun cloneLocation(location: Location): Location {
+        // smart cast of location.start to 'Position' is impossible, because 'location.start' is a
+        // public API property declared in different module
+        val locationStart = location.start
+        return if (locationStart == null) {
+            Location.create(location.file)
+        } else {
+            Location.create(location.file, locationStart, location.end)
+        }
+    }
+
+    private enum class Method(
+        val className: String,
+        val methodName: String,
+        val args: Array<String>
+    ) {
+        BINDER_CLEAR_CALLING_IDENTITY(CLASS_BINDER, "clearCallingIdentity", emptyArray()),
+        BINDER_RESTORE_CALLING_IDENTITY(CLASS_BINDER, "restoreCallingIdentity", arrayOf("long")),
+        BINDER_GET_CALLING_PID(CLASS_BINDER, "getCallingPid", emptyArray()),
+        BINDER_GET_CALLING_UID(CLASS_BINDER, "getCallingUid", emptyArray()),
+        BINDER_GET_CALLING_UID_OR_THROW(CLASS_BINDER, "getCallingUidOrThrow", emptyArray()),
+        BINDER_GET_CALLING_USER_HANDLE(CLASS_BINDER, "getCallingUserHandle", emptyArray()),
+        USER_HANDLE_GET_CALLING_APP_ID(CLASS_USER_HANDLE, "getCallingAppId", emptyArray()),
+        USER_HANDLE_GET_CALLING_USER_ID(CLASS_USER_HANDLE, "getCallingUserId", emptyArray())
+    }
+
+    private data class Token(
+        val variableName: String,
+        val scope: SearchScope?,
+        val location: Location,
+        val finallyBlock: UElement?
+    )
+}
diff --git a/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt
new file mode 100644
index 0000000..1bd63ea
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/android/lint/CallingIdentityTokenIssueRegistry.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+// TODO: uncomment when lint API in Soong becomes 30.0+
+// import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenIssueRegistry : IssueRegistry() {
+    override val issues = listOf(
+            ISSUE_UNUSED_TOKEN,
+            ISSUE_NON_FINAL_TOKEN,
+            ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+            ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+            ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+            ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+    )
+
+    override val api: Int
+        get() = CURRENT_API
+
+    override val minApi: Int
+        get() = 8
+
+//    TODO: uncomment when lint API in Soong becomes 30.0+
+//    override val vendor: Vendor = Vendor(
+//            vendorName = "Android Open Source Project",
+//            feedbackUrl = "http://b/issues/new?component=315013",
+//            contact = "brufino@google.com"
+//    )
+
+    companion object {
+        /** Issue: unused token from Binder.clearCallingIdentity() */
+        @JvmField
+        val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
+                id = "UnusedTokenOfOriginalCallingIdentity",
+                briefDescription = "Unused token of Binder.clearCallingIdentity()",
+                explanation = """
+                    You cleared the original calling identity with \
+                    `Binder.clearCallingIdentity()`, but have not used the returned token to \
+                    restore the identity.
+
+                    Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
+                    of the method or when you need to restore the identity.
+
+                    `token` is the result of `Binder.clearCallingIdentity()`
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has not been " +
+                "used to restore the calling identity. Introduce a `try`-`finally` after the " +
+                "declaration and call `Binder.restoreCallingIdentity($variableName)` in " +
+                "`finally` or remove `$variableName`."
+
+        /** Issue: non-final token from Binder.clearCallingIdentity() */
+        @JvmField
+        val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
+                id = "NonFinalTokenOfOriginalCallingIdentity",
+                briefDescription = "Non-final token of Binder.clearCallingIdentity()",
+                explanation = """
+                    You cleared the original calling identity with \
+                    `Binder.clearCallingIdentity()`, but have not made the returned token `final`.
+
+                    The token should be `final` in order to prevent it from being overwritten, \
+                    which can cause problems when restoring the identity with \
+                    `Binder.restoreCallingIdentity(token)`.
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is a " +
+                "non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
+                "`$variableName`."
+
+        /** Issue: nested calls of Binder.clearCallingIdentity() */
+        @JvmField
+        val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
+                id = "NestedClearCallingIdentityCalls",
+                briefDescription = "Nested calls of Binder.clearCallingIdentity()",
+                explanation = """
+                    You cleared the original calling identity with \
+                    `Binder.clearCallingIdentity()` twice without restoring identity with the \
+                    result of the first call.
+
+                    Make sure to restore the identity after each clear identity call.
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageNestedClearIdentityCallsPrimary(
+            firstCallVariableName: String,
+            secondCallVariableName: String
+        ): String = "The calling identity has already been cleared and returned into " +
+                "`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
+                "restoring the calling identity with " +
+                "`Binder.restoreCallingIdentity($firstCallVariableName)`."
+
+        fun getIncidentMessageNestedClearIdentityCallsSecondary(
+            firstCallVariableName: String
+        ): String = "Location of the `$firstCallVariableName` declaration."
+
+        /** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
+        @JvmField
+        val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
+                id = "ClearIdentityCallNotFollowedByTryFinally",
+                briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
+                        "statement",
+                explanation = """
+                    You cleared the original calling identity with \
+                    `Binder.clearCallingIdentity()`, but the next statement is not a `try` \
+                    statement.
+
+                    Use the following pattern for running operations with your own identity:
+
+                    ```
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        // Code using your own identity
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                    ```
+
+                    Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
+                    statement risk throwing an exception without doing a safe and unconditional \
+                    restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
+                    child of the `finally` block. If you do not follow the pattern, you may run \
+                    code with your identity that was originally intended to run with the calling \
+                    application's identity.
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
+            variableName: String
+        ): String = "You cleared the calling identity and returned the result into " +
+                "`$variableName`, but the next statement is not a `try`-`finally` statement. " +
+                "Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
+                "safe restore of the calling identity by calling " +
+                "`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
+                "of the `finally` block."
+
+        /** Issue: Binder.restoreCallingIdentity() is not in finally block */
+        @JvmField
+        val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
+                id = "RestoreIdentityCallNotInFinallyBlock",
+                briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
+                explanation = """
+                    You are restoring the original calling identity with \
+                    `Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
+                    the `finally` block of the `try` statement.
+
+                    Use the following pattern for running operations with your own identity:
+
+                    ```
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        // Code using your own identity
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                    ```
+
+                    If you do not surround the code using your identity with the `try` statement \
+                    and call `Binder.restoreCallingIdentity()` as an immediate child of the \
+                    `finally` block, you may run code with your identity that was originally \
+                    intended to run with the calling application's identity.
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName: String): String =
+                "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of the " +
+                        "`finally` block of the try statement after `$variableName` declaration. " +
+                        "Surround the call with `finally` block and call it unconditionally."
+
+        /** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
+        @JvmField
+        val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
+                id = "UseOfCallerAwareMethodsWithClearedIdentity",
+                briefDescription = "Use of caller-aware methods after " +
+                        "Binder.clearCallingIdentity()",
+                explanation = """
+                    You cleared the original calling identity with \
+                    `Binder.clearCallingIdentity()`, but used one of the methods below before \
+                    restoring the identity. These methods will use your own identity instead of \
+                    the caller's identity, so if this is expected replace them with methods that \
+                    explicitly query your own identity such as `Process.myUid()`, \
+                    `Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
+                    out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
+                    section.
+
+                    ```
+                    Binder.getCallingPid()
+                    Binder.getCallingUid()
+                    Binder.getCallingUidOrThrow()
+                    Binder.getCallingUserHandle()
+                    UserHandle.getCallingAppId()
+                    UserHandle.getCallingUserId()
+                    ```
+                    """,
+                category = Category.SECURITY,
+                priority = 6,
+                severity = Severity.WARNING,
+                implementation = Implementation(
+                        CallingIdentityTokenDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                )
+        )
+
+        fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+            variableName: String,
+            methodName: String
+        ): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
+                "and returned into `$variableName`, so `$methodName` will be using your own " +
+                "identity instead of the caller's. Either explicitly query your own identity or " +
+                "move it after restoring the identity with " +
+                "`Binder.restoreCallingIdentity($variableName)`."
+    }
+}
diff --git a/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt b/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt
new file mode 100644
index 0000000..0fd1a76
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/android/lint/CallingIdentityTokenDetectorTest.kt
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = CallingIdentityTokenDetector()
+
+    override fun getIssues(): List<Issue> = listOf(
+            CallingIdentityTokenIssueRegistry.ISSUE_UNUSED_TOKEN,
+            CallingIdentityTokenIssueRegistry.ISSUE_NON_FINAL_TOKEN,
+            CallingIdentityTokenIssueRegistry.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+            CallingIdentityTokenIssueRegistry.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+            CallingIdentityTokenIssueRegistry
+                    .ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+            CallingIdentityTokenIssueRegistry.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+    )
+
+    /** No issue scenario */
+
+    fun testDoesNotDetectIssuesInCorrectScenario() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethod() {
+                            final long token1 = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token1);
+                            }
+                            final long token2 = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                android.os.Binder.restoreCallingIdentity(token2);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expectClean()
+    }
+
+    /** Unused token issue tests */
+
+    fun testDetectsUnusedTokens() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodImported() {
+                            final long token1 = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                        }
+                        private void testMethodFullClass() {
+                            final long token2 = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:5: Warning: token1 has not been used to \
+                        restore the calling identity. Introduce a try-finally after the \
+                        declaration and call Binder.restoreCallingIdentity(token1) in finally or \
+                        remove token1. [UnusedTokenOfOriginalCallingIdentity]
+                                final long token1 = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:11: Warning: token2 has not been used to \
+                        restore the calling identity. Introduce a try-finally after the \
+                        declaration and call Binder.restoreCallingIdentity(token2) in finally or \
+                        remove token2. [UnusedTokenOfOriginalCallingIdentity]
+                                final long token2 = android.os.Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 2 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDetectsUnusedTokensInScopes() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodTokenFromClearIdentity() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                        }
+                        private void testMethodTokenNotFromClearIdentity() {
+                            long token = 0;
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+                        restore the calling identity. Introduce a try-finally after the \
+                        declaration and call Binder.restoreCallingIdentity(token) in finally or \
+                        remove token. [UnusedTokenOfOriginalCallingIdentity]
+                                final long token = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 1 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDoesNotDetectUsedTokensInScopes() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodTokenFromClearIdentity() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                        private void testMethodTokenNotFromClearIdentity() {
+                            long token = 0;
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expectClean()
+    }
+
+    fun testDetectsUnusedTokensWithSimilarNamesInScopes() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethod1() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                        }
+                        private void testMethod2() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+                        restore the calling identity. Introduce a try-finally after the \
+                        declaration and call Binder.restoreCallingIdentity(token) in finally or \
+                        remove token. [UnusedTokenOfOriginalCallingIdentity]
+                                final long token = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:11: Warning: token has not been used to \
+                        restore the calling identity. Introduce a try-finally after the \
+                        declaration and call Binder.restoreCallingIdentity(token) in finally or \
+                        remove token. [UnusedTokenOfOriginalCallingIdentity]
+                                final long token = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 2 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** Non-final token issue tests */
+
+    fun testDetectsNonFinalTokens() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethod() {
+                            long token1 = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token1);
+                            }
+                            long token2 = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                android.os.Binder.restoreCallingIdentity(token2);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:5: Warning: token1 is a non-final token from \
+                        Binder.clearCallingIdentity(). Add final keyword to token1. \
+                        [NonFinalTokenOfOriginalCallingIdentity]
+                                long token1 = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:10: Warning: token2 is a non-final token from \
+                        Binder.clearCallingIdentity(). Add final keyword to token2. \
+                        [NonFinalTokenOfOriginalCallingIdentity]
+                                long token2 = android.os.Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 2 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** Nested clearCallingIdentity() calls issue tests */
+
+    fun testDetectsNestedClearCallingIdentityCalls() {
+        // Pattern: clear - clear - clear - restore - restore - restore
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethod() {
+                            final long token1 = Binder.clearCallingIdentity();
+                            try {
+                                final long token2 = android.os.Binder.clearCallingIdentity();
+                                try {
+                                    final long token3 = Binder.clearCallingIdentity();
+                                    try {
+                                    } finally {
+                                        Binder.restoreCallingIdentity(token3);
+                                    }
+                                } finally {
+                                    android.os.Binder.restoreCallingIdentity(token2);
+                                }
+                            } finally {
+                                Binder.restoreCallingIdentity(token1);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:7: Warning: The calling identity has already \
+                        been cleared and returned into token1. Move token2 declaration after \
+                        restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+                        [NestedClearCallingIdentityCalls]
+                                    final long token2 = android.os.Binder.clearCallingIdentity();
+                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                            src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+                        src/test/pkg/TestClass1.java:9: Warning: The calling identity has already \
+                        been cleared and returned into token1. Move token3 declaration after \
+                        restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+                        [NestedClearCallingIdentityCalls]
+                                        final long token3 = Binder.clearCallingIdentity();
+                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                            src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+                        0 errors, 2 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** clearCallingIdentity() not followed by try-finally issue tests */
+
+    fun testDetectsClearIdentityCallNotFollowedByTryFinally() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodNoTry() {
+                            final long token = Binder.clearCallingIdentity();
+                            Binder.restoreCallingIdentity(token);
+                        }
+                        private void testMethodSomethingBetweenClearAndTry() {
+                            final long token = Binder.clearCallingIdentity();
+                            int pid = 0;
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                        private void testMethodLocalVariableBetweenClearAndTry() {
+                            final long token = Binder.clearCallingIdentity(), num = 0;
+                            try {
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                        private void testMethodTryCatch() {
+                            final long token = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } catch (Exception e) {
+                            }
+                            Binder.restoreCallingIdentity(token);
+                        }
+                        private void testMethodTryCatchInScopes() {
+                            final long token = android.os.Binder.clearCallingIdentity();
+                            {
+                                try {
+                                } catch (Exception e) {
+                                }
+                            }
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:5: Warning: You cleared the calling identity \
+                        and returned the result into token, but the next statement is not a \
+                        try-finally statement. Define a try-finally block after token declaration \
+                        to ensure a safe restore of the calling identity by calling \
+                        Binder.restoreCallingIdentity(token) and making it an immediate child of \
+                        the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+                                final long token = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:9: Warning: You cleared the calling identity \
+                        and returned the result into token, but the next statement is not a \
+                        try-finally statement. Define a try-finally block after token declaration \
+                        to ensure a safe restore of the calling identity by calling \
+                        Binder.restoreCallingIdentity(token) and making it an immediate child of \
+                        the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+                                final long token = Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:17: Warning: You cleared the calling identity \
+                        and returned the result into token, but the next statement is not a \
+                        try-finally statement. Define a try-finally block after token declaration \
+                        to ensure a safe restore of the calling identity by calling \
+                        Binder.restoreCallingIdentity(token) and making it an immediate child of \
+                        the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+                                final long token = Binder.clearCallingIdentity(), num = 0;
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:24: Warning: You cleared the calling identity \
+                        and returned the result into token, but the next statement is not a \
+                        try-finally statement. Define a try-finally block after token declaration \
+                        to ensure a safe restore of the calling identity by calling \
+                        Binder.restoreCallingIdentity(token) and making it an immediate child of \
+                        the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+                                final long token = android.os.Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:31: Warning: You cleared the calling identity \
+                        and returned the result into token, but the next statement is not a \
+                        try-finally statement. Define a try-finally block after token declaration \
+                        to ensure a safe restore of the calling identity by calling \
+                        Binder.restoreCallingIdentity(token) and making it an immediate child of \
+                        the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+                                final long token = android.os.Binder.clearCallingIdentity();
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 5 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** restoreCallingIdentity() call not in finally block issue tests */
+
+    fun testDetectsRestoreCallingIdentityCallNotInFinally() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodImported() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } catch (Exception e) {
+                            } finally {
+                            }
+                            Binder.restoreCallingIdentity(token);
+                        }
+                        private void testMethodFullClass() {
+                            final long token = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                            android.os.Binder.restoreCallingIdentity(token);
+                        }
+                        private void testMethodRestoreInCatch() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                            } catch (Exception e) {
+                                Binder.restoreCallingIdentity(token);
+                            } finally {
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:10: Warning: \
+                        Binder.restoreCallingIdentity(token) is not an immediate child of the \
+                        finally block of the try statement after token declaration. Surround the c\
+                        all with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                Binder.restoreCallingIdentity(token);
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:17: Warning: \
+                        Binder.restoreCallingIdentity(token) is not an immediate child of the \
+                        finally block of the try statement after token declaration. Surround the c\
+                        all with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                android.os.Binder.restoreCallingIdentity(token);
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:23: Warning: \
+                        Binder.restoreCallingIdentity(token) is not an immediate child of the \
+                        finally block of the try statement after token declaration. Surround the c\
+                        all with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                    Binder.restoreCallingIdentity(token);
+                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 3 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    fun testDetectsRestoreCallingIdentityCallNotInFinallyInScopes() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    public class TestClass1 {
+                        private void testMethodOutsideFinally() {
+                            final long token1 = Binder.clearCallingIdentity();
+                            try {
+                            } catch (Exception e) {
+                            } finally {
+                            }
+                            {
+                                Binder.restoreCallingIdentity(token1);
+                            }
+                            final long token2 = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                            }
+                            {
+                                {
+                                    {
+                                        android.os.Binder.restoreCallingIdentity(token2);
+                                    }
+                                }
+                            }
+                        }
+                        private void testMethodInsideFinallyInScopes() {
+                            final long token1 = Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                {
+                                    {
+                                        Binder.restoreCallingIdentity(token1);
+                                    }
+                                }
+                            }
+                            final long token2 = android.os.Binder.clearCallingIdentity();
+                            try {
+                            } finally {
+                                {
+                                    {
+                                        android.os.Binder.restoreCallingIdentity(token2);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:11: Warning: \
+                        Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+                        finally block of the try statement after token1 declaration. Surround the \
+                        call with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                    Binder.restoreCallingIdentity(token1);
+                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:20: Warning: \
+                        Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+                        finally block of the try statement after token2 declaration. Surround the \
+                        call with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                            android.os.Binder.restoreCallingIdentity(token2);
+                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:31: Warning: \
+                        Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+                        finally block of the try statement after token1 declaration. Surround the \
+                        call with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                            Binder.restoreCallingIdentity(token1);
+                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:40: Warning: \
+                        Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+                        finally block of the try statement after token2 declaration. Surround the \
+                        call with finally block and call it unconditionally. \
+                        [RestoreIdentityCallNotInFinallyBlock]
+                                            android.os.Binder.restoreCallingIdentity(token2);
+                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 4 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** Use of caller-aware methods after clearCallingIdentity() issue tests */
+
+    fun testDetectsUseOfCallerAwareMethodsWithClearedIdentityIssuesInScopes() {
+        lint().files(
+                java(
+                    """
+                    package test.pkg;
+                    import android.os.Binder;
+                    import android.os.UserHandle;
+                    public class TestClass1 {
+                        private void testMethod() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                int pid1 = Binder.getCallingPid();
+                                int pid2 = android.os.Binder.getCallingPid();
+                                int uid1 = Binder.getCallingUid();
+                                int uid2 = android.os.Binder.getCallingUid();
+                                int uid3 = Binder.getCallingUidOrThrow();
+                                int uid4 = android.os.Binder.getCallingUidOrThrow();
+                                UserHandle uh1 = Binder.getCallingUserHandle();
+                                UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+                                {
+                                    int appId1 = UserHandle.getCallingAppId();
+                                    int appId2 = android.os.UserHandle.getCallingAppId();
+                                    int userId1 = UserHandle.getCallingUserId();
+                                    int userId2 = android.os.UserHandle.getCallingUserId();
+                                }
+                            } finally {
+                            Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                        """
+                        src/test/pkg/TestClass1.java:8: Warning: You cleared the original identity \
+                        with Binder.clearCallingIdentity() and returned into token, so \
+                        Binder.getCallingPid() will be using your own identity instead of the \
+                        caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int pid1 = Binder.getCallingPid();
+                                               ~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:9: Warning: You cleared the original identity \
+                        with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.Binder.getCallingPid() will be using your own identity instead \
+                        of the caller's. Either explicitly query your own identity or move it \
+                        after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int pid2 = android.os.Binder.getCallingPid();
+                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:10: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        Binder.getCallingUid() will be using your own identity instead of the \
+                        caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int uid1 = Binder.getCallingUid();
+                                               ~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:11: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.Binder.getCallingUid() will be using your own identity instead \
+                        of the caller's. Either explicitly query your own identity or move it \
+                        after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int uid2 = android.os.Binder.getCallingUid();
+                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:12: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        Binder.getCallingUidOrThrow() will be using your own identity instead of \
+                        the caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int uid3 = Binder.getCallingUidOrThrow();
+                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:13: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.Binder.getCallingUidOrThrow() will be using your own identity \
+                        instead of the caller's. Either explicitly query your own identity or move \
+                        it after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    int uid4 = android.os.Binder.getCallingUidOrThrow();
+                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:14: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        Binder.getCallingUserHandle() will be using your own identity instead of \
+                        the caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    UserHandle uh1 = Binder.getCallingUserHandle();
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:15: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.Binder.getCallingUserHandle() will be using your own identity \
+                        instead of the caller's. Either explicitly query your own identity or move \
+                        it after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                    UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:17: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        UserHandle.getCallingAppId() will be using your own identity instead of \
+                        the caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                        int appId1 = UserHandle.getCallingAppId();
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:18: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.UserHandle.getCallingAppId() will be using your own identity \
+                        instead of the caller's. Either explicitly query your own identity or move \
+                        it after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                        int appId2 = android.os.UserHandle.getCallingAppId();
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:19: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        UserHandle.getCallingUserId() will be using your own identity instead of \
+                        the caller's. Either explicitly query your own identity or move it after \
+                        restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                        int userId1 = UserHandle.getCallingUserId();
+                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        src/test/pkg/TestClass1.java:20: Warning: You cleared the original \
+                        identity with Binder.clearCallingIdentity() and returned into token, so \
+                        android.os.UserHandle.getCallingUserId() will be using your own identity \
+                        instead of the caller's. Either explicitly query your own identity or move \
+                        it after restoring the identity with Binder.restoreCallingIdentity(token). \
+                        [UseOfCallerAwareMethodsWithClearedIdentity]
+                                        int userId2 = android.os.UserHandle.getCallingUserId();
+                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        0 errors, 12 warnings
+                        """.addLineContinuation()
+                )
+    }
+
+    /** Stubs for classes used for testing */
+
+    private val binderStub: TestFile = java(
+            """
+            package android.os;
+            public class Binder {
+                public static final native long clearCallingIdentity() {
+                    return 0;
+                }
+                public static final native void restoreCallingIdentity(long token) {
+                }
+                public static final native int getCallingPid() {
+                    return 0;
+                }
+                public static final native int getCallingUid() {
+                    return 0;
+                }
+                public static final int getCallingUidOrThrow() {
+                    return 0;
+                }
+                public static final @NonNull UserHandle getCallingUserHandle() {
+                    return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+                }
+            }
+            """
+    ).indented()
+
+    private val userHandleStub: TestFile = java(
+            """
+            package android.os;
+            import android.annotation.AppIdInt;
+            import android.annotation.UserIdInt;
+            public class UserHandle {
+                public static @AppIdInt int getCallingAppId() {
+                    return getAppId(Binder.getCallingUid());
+                }
+                public static @UserIdInt int getCallingUserId() {
+                    return getUserId(Binder.getCallingUid());
+                }
+                public static @UserIdInt int getUserId(int uid) {
+                    return 0;
+                }
+                public static @AppIdInt int getAppId(int uid) {
+                    return 0;
+                }
+                public static UserHandle of(@UserIdInt int userId) {
+                    return new UserHandle();
+                }
+            }
+            """
+    ).indented()
+
+    private val userIdIntStub: TestFile = java(
+            """
+            package android.annotation;
+            public @interface UserIdInt {
+            }
+            """
+    ).indented()
+
+    private val appIdIntStub: TestFile = java(
+            """
+            package android.annotation;
+            public @interface AppIdInt {
+            }
+            """
+    ).indented()
+
+    private val stubs = arrayOf(binderStub, userHandleStub, userIdIntStub, appIdIntStub)
+
+    // Substitutes "backslash + new line" with an empty string to imitate line continuation
+    private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}